In NodeJS, how do I make database queries inside of a Promise? - node.js

NodeJS 6.9.3
What I previously had went like this:
An outer function called "get_user()":
return database_queries.get_user(user_name)
.then(function(results_from_database) {
and that function then ran a database call, using Knex, and returned:
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
When I did this, the database query ran, and got data, but this happened asynchronously, without the results ever being returned to the outer function. In other words, the outer function had completed long before the database queries had run.
So I tried to wrap the inner function in a Promise:
function get_user(user_name) {
return new Promise(function(resolve, reject) {
resolve ()
.then(function() {
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
Now the database calls don't seem to ever be called. When they run, they appear in the logs, but now there are no database queries appearing in the logs. It would seem this inner function now returns a Promise, but the "resolve()" part of the Promise is never called.
What am I doing wrong here?

Here is a bit simpler way to write essentially the same query:
function get_user(user_name) {
const regex = `[[:<:]]${user_name}[[:>:]]`;
return DB.knex('user_profile')
.where(DB.knex.raw(`?? REGEXP ?`, ['name', regex]))
.then(res => {
// do what ever you like with returned rows here
});
}

You might want to simplify that a bit.
Take a look at these example projects.
https://github.com/joicenunes/helloapp
https://github.com/joicenunes/exercicio02
https://github.com/joicenunes/exercicio03
https://github.com/joicenunes/exercicio-04
(there are more, but you can find the rest)
Also avoid "replaceAll" and use binding variables.
Finally, since you are using node 6.x, you can use a few es6 goodies (arrow functions, multi-line strings, classes, etc), make the language work for you.

Related

Running viewer.getProperties for multiple elements in parallel and then handling the result

I am using the viewer.getProperties(dbId, onSuccessCallback, onErrorCallback) method in order to get properties for objects in my viewer. I want to run the method for all selected objects, extract a subset of the properties for each object, and present the subsets in a table.
var subsets = [];
var selectFunctions = [];
handleSelection(selection, addProps, onError);
function handleSelection(selection, onSuccess, onError) {
for (var i = 0; i < selection.length; i++)
selectFunctions.push(_viewer.getProperties(selection[i], onSuccess, onError));
}
function addProps(data) {
var props = [];
for (var prop in data.properties) {
//Add property to props if some condition is true...
}
subsets.push(props);
}
Promise.all(_selectFunctions).then(function () {
console.log("Handled all selections");
//Add subsets to table...
}).catch(function (error) {
console.log("ERRROR");
});
Since getProperties is running asynchronously I am not able to wait for all objects before the table is updated. The table is updated with one object at a time, and we would rather update all at once. Blocking IO is not a problem.
As the could shows I have been looking into Promise.all() from bluebird.js in order to control execution and wait for all getProperties calls to return, but so far unsuccessfully.
Regards,
Torjus
This question is purely unrelated to the use of the viewer, you would need to look for some documentation on how to use Promises in order to wait for completion of multiple requests in parallel.
here is some pseudo code that may help you (ES6 syntax), I'm skipping error handling for sake of clarity:
// wrap get the async method in a promise so you can wait its completion
const getPropertiesAsync = (id) => {
return new Promise((resolve, reject) => {
_viewer.getProperties(id, (result) => {
resolve(result)
}, (error) => {
reject(error)
})
})
}
//create an array of asynchronous tasks for each component you want to get props on
const propTasks = componentIds.map((id) => {
return getPropertiesAsync(id)
})
//promise version
Promise.all(propTasks).then((results) => {
//populate table with results
})
//OR async ES7 syntax
const results = await Promise.all(propTasks)
//populate table with results
Here is an article I wrote about using async/await with the viewer, but since the topic is much broader you should be able to find a lot more documentation by looking over the web by yourself:
Getting rid of JavaScript callbacks using async/await
Hope that helps

Sequelize migration - run an array of queries in order

I have a series of queries that need to run in a specific order. I've been trying this:
var queries = []
queries.push('update blah set foo="bar"')
queries.push('update baz set bar="foo"')
for(var i=0; i< queries.length; i++){
Promise.all([
migration.sequelize.query(queries[i]).then(function(result){
console.log(result)
})
])
}
done();
This does not work as anticipated. Any suggestions?
UPDATE: using recursion in the callback seems to work
var queries = []
queries.push('update blah set foo="bar"')
queries.push('update baz set bar="foo"')
var index = 0
var execute = function(queries){
if(typeof queries[index] == 'undefined'){
return done()
}
console.log(queries[index])
migration.sequelize.query(queries[index]).then(function(result){
console.log(result)
index += 1
return execute(queries)
})
}
execute(queries)
If you're on io.js and can write generators, or transpiling using Babel or TypeScript and can write async functions, this becomes very easy.
async function runSerialQueries(queries) {
var results = [];
for (var i=0; i<queries.length; i++) {
var query = queries[i];
var result = await migration.sequelize.query(query);
results.push(result);
}
return results;
}
runSerialQueries([
'update blah set foo="bar"',
'update baz set bar="foo"'
]).then(function(results) {
// ...
})
NOTE: The async keyword used above is a native feature of JavaScript EcmaScript 2016, not to be confused with the "async" npm module which is a completely different thing.
Anyhow, this solution ensures that one query doesn't start until the previous one ends, and will go in the order of the original array. It basically behaves just like it reads. If you can write generators but not async functions, something nearly identical can be done with generators, but requires a library like co() or Bluebird.coroutine().
Check out this excellent article for background on these techniques. They really are the future of JavaScript:
https://blog.risingstack.com/asynchronous-javascript/ <-- highly recommended!
I don't exactly know what the problem is with your code (if there any).
You should try with the each function of the async lib.
For example:
async.each(queries, function(query, done) {
// do the query here
done(); // this query is done, so runs the next one
});

How to use promise bluebird in nested for loop?

I need to use bluebird in my code and I have no idea how to use it. My code contains nested loops. When the user logs in, my code will run. It will begin to look for any files under the user, and if there are files then, it will loop through to get the name of the files, since the name is stored in a dictionary. Once it got the name, it will store the name in an array. Once all the names are stored, it will be passed along in res.render().
Here is my code:
router.post('/login', function(req, res){
var username = req.body.username;
var password = req.body.password;
Parse.User.logIn(username, password, {
success: function(user){
var Files = Parse.Object.extend("File");
var object = [];
var query = new Parse.Query(Files);
query.equalTo("user", Parse.User.current());
var temp;
query.find({
success:function(results){
for(var i=0; i< results.length; i++){
var file = results[i].toJSON();
for(var k in file){
if (k ==="javaFile"){
for(var t in file[k]){
if (t === "name"){
temp = file[k][t];
var getname = temp.split("-").pop();
object[i] = getname;
}
}
}
}
}
}
});
console.log(object);
res.render('filename', {title: 'File Name', FIles: object});
console.log(object);
},
error: function(user, error) {
console.log("Invalid username/password");
res.render('logins');
}
})
});
EDIT:The code doesn't work, because on the first and second console.log(object), I get an empty array. I am suppose to get one item in that array, because I have one file saved
JavaScript code is all parsed from top to bottom, but it doesn't necessarily execute in that order with asynchronous code. The problem is that you have the log statements inside of the success callback of your login function, but it's NOT inside of the query's success callback.
You have a few options:
Move the console.log statements inside of the inner success callback so that while they may be parsed at load time, they do not execute until both callbacks have been invoked.
Promisify functions that traditionally rely on and invoke callback functions, and hang then handlers off of the returned value to chain the promises together.
The first option is not using promises at all, but relying solely on callbacks. To flatten your code you will want to promisify the functions and then chain them.
I'm not familiar with the syntax you're using there with the success and error callbacks, nor am I familiar with Parse. Typically you would do something like:
query.find(someArgsHere, function(success, err) {
});
But then you would have to nest another callback inside of that, and another callback inside of that. To "flatten" the pyramid, we make the function return a promise instead, and then we can chain the promises. Assuming that Parse.User.logIn is a callback-style function (as is Parse.Query.find), you might do something like:
var Promise = require('bluebird');
var login = Promise.promisify(Parse.User.logIn);
var find = Promise.promisify(Parse.Query.find);
var outerOutput = [];
return login(yourArgsHere)
.then(function(user) {
return find(user.someValue);
})
.then(function(results) {
var innerOutput = [];
// do something with innerOutput or outerOutput and render it
});
This should look familiar to synchronous code that you might be used to, except instead of saving the returned value into a variable and then passing that variable to your next function call, you use "then" handlers to chain the promises together. You could either create the entire output variable inside of the second then handler, or you can declare the variable output prior to even starting this promise chain, and then it will be in scope for all of those functions. I have shown you both options above, but obviously you don't need to define both of those variables and assign them values. Just pick the option that suits your needs.
You can also use Bluebird's promisifyAll() function to wrap an entire library with equivalent promise-returning functions. They will all have the same name of the functions in the library suffixed with Async. So assuming the Parse library contains callback-style functions named someFunctionName() and someOtherFunc() you could do this:
var Parse = Promise.promisifyAll(require("Parse"));
var promiseyFunction = function() {
return Parse.someFunctionNameAsync()
.then(function(result) {
return Parse.someOtherFuncAsync(result.someProperty);
})
.then(function(otherFuncResult) {
var something;
// do stuff to assign a value to something
return something;
});
}
I have a few pointers. ... Btw tho, are you trying to use Parse's Promises?
You can get rid of those inner nested loops and a few other changes:
Use some syntax like this to be more elegant:
/// You could use a map function like this to get the files into an array of just thier names
var fileNames = matchedFiles.map(function _getJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.split('-')[0]; // RETURN first part of name
});
// Example to filter/retrieve only valid file objs (with dashes in name)
var matchedFiles = results.filter(function _hasJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.indexOf('-') > -1; // and has a dash
});
And here is an example on using Parse's native promises (add code above to line 4/5 below, note the 'then()' function, that's effectively now your 'callback' handler):
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.select("score", "playerName");
query.find().then(function(results) {
// each of results will only have the selected fields available.
});

Node.js promises with mongoskin

I'm trying to avoid using callbacks when making mongodb queries. I'm using mongoskin to make calls like so:
req.db.collection('users').find().toArray(function (err, doc) {
res.json(doc);
});
In many cases I need to make multiple queries so I want to use Node.js promise library but I'm not sure how to wrap these functions as promises. Most of the examples I see are trivial for things like readFile, I'm guessing in this case I would need to wrap toArray somehow? Can this be done or would have to be something implemented by mongoskin?
An example could be any set of callbacks, find/insert, find/find/insert, find/update:
req.db.collection('users').find().toArray(function (err, doc) {
if (doc) {
req.db.collection('users').find().toArray(function (err, doc) {
// etc...
});
}
else {
// err
}
});
You can promisify the entire module like so with bluebird:
var Promise = require("bluebird");
var mongoskin = require("mongoskin");
Object.keys(mongoskin).forEach(function(key) {
var value = mongoskin[key];
if (typeof value === "function") {
Promise.promisifyAll(value);
Promise.promisifyAll(value.prototype);
}
});
Promise.promisifyAll(mongoskin);
This only needs to be done in one place for one time in your application, not anywhere in your application code.
After that you just use methods normally except with the Async suffix and don't pass callbacks:
req.db.collection('users').find().toArrayAsync()
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
});
So again, if you call a function like
foo(a, b, c, function(err, result) {
if (err) return console.log(err);
//Code
});
The promise-returning version is called like:
fooAsync(a, b, c).then(...)
(Uncaught errors are automatically logged so you don't need to check for them if you are only going to log it)
Just stumbled here with the same question and didn't love "promisfying" mongoskin so did a bit more digging and found monk. It's built on top of mongoskin, tidies up the API and returns
promises for all async calls. Probably worth a peek to anyone else who lands here.
Esailija's answer may work, but its not super efficient since you have to run db.collection on every single db call. I don't know exactly how expensive that is, but looking at the code in mongoskin, its non-trivial. Not only that, but it's globally modifying prototypes, which isn't very safe.
The way I do this with fibers futures is:
wrap the collection methods for each collection
on receiving the result, for methods that return a Cursor wrap the toArray method, call it and return the resulting future (for methods that don't return a cursor, you don't need to do anything else).
use the future as normal
like this:
var Future = require("fibers/future")
// note: when i originally wrote this answer fibers/futures didn't have a good/intuitive wrapping function; but as of 2014-08-18, it does have one
function futureWrap() {
// function
if(arguments.length === 1) {
var fn = arguments[0]
var object = undefined
// object, methodName
} else {
var object = arguments[0]
var fn = object[arguments[1]]
}
return function() {
var args = Array.prototype.slice.call(arguments)
var future = new Future
args.push(future.resolver())
var me = this
if(object) me = object
fn.apply(me, args)
return future
}
}
var methodsYouWantToHave = ['findOne', 'find', 'update', 'insert', 'remove', 'findAndModify']
var methods = {}
methodsYouWantToHave.forEach(function(method) {
internalMethods[method] = futureWrap(this.collection, method)
}.bind(this))
// use them
var document = methods.findOne({_id: 'a3jf938fj98j'}, {}).wait()
var documents = futureWrap(methods.find({x: 'whatever'}, {}).wait(), 'toArray')().wait()
If you don't want to use fibers, I'd recommend using the async-future module, which has a good wrap function built in too.

Returning an Array using Firebase

Trying to find the best-use example of returning an array of data in Node.js with Q library (or any similar library, I'm not partial) when using Firebase .on("child_added");
I've tried using Q.all() but it never seems to wait for the promises to fill before returning. This is my current example:
function getIndex()
{
var deferred = q.defer();
deferred.resolve(new FirebaseIndex( Firebase.child('users').child(user.app_user_id).child('posts'), Firebase.child('posts') ) );
return deferred.promise;
}
function getPost( post )
{
var deferred = q.defer();
deferred.resolve(post.val());
return deferred.promise;
}
function getPosts()
{
var promises = [];
getIndex().then( function (posts) {
posts.on( 'child_added', function (_post) {
promises.push( getPost(_post) );
});
});
return q.all(promises);
}
The problem occurs in getPosts(). It pushes a promise into your array inside an async function--that won't work since q.all is called before the promise objects have been added.
Also, child_added is a real-time event notification. You can't use that as a way to grab "all of the data" because there is no such thing as "all"; the data is constantly changing in real-time environments. FirebaseIndex is also using child_added callbacks internally, so that's not going to work with this use case either.
You can grab all of the posts using the 'value' callback (but not a specific subset of records) as follows:
function getPosts() {
var def = q.defer();
Firebase.child('users').once('value', function(snap) {
var records = [];
snap.forEach(function(ss) {
records.push( ss.val() );
});
def.resolve(records);
});
return def.promise;
}
But at this point, it's time to consider things in terms of real-time environments. Most likely, there is no reason "all" data needs to be present before getting to work.
Consider just grabbing each record as they come in and appending them to whatever DOM or Array where they need to be stored, and working from an event driven model instead of a GET/POST centered approach.
With luck, you can bypass this use case entirely.

Resources