Node.js: How to run asynchronous code sequentially - javascript

I have this chunk of code
User.find({}, function(err, users) {
for (var i = 0; i < users.length; i++) {
pseudocode
Friend.find({
'user': curUser._id
}, function(err, friends) * * ANOTHER CALLBACK * * {
for (var i = 0; i < friends.length; i++) {
pseudocode
}
console.log("HERE I'm CHECKING " + curUser);
if (curUser.websiteaccount != "None") {
request.post({
url: 'blah',
formData: blah
}, function(err, httpResponse, body) { * * ANOTHER CALLBACK * *
pseudocode
sendMail(friendResults, curUser);
});
} else {
pseudocode
sendMail(friendResults, curUser);
}
});
console.log("finished friend");
console.log(friendResults);
sleep.sleep(15);
console.log("finished waiting");
console.log(friendResults);
}
});
There's a couple asynchronous things happening here. For each user, I want to find their relevant friends and concat them to a variable. I then want to check if that user has a website account, and if so, make a post request and grab some information there. Only thing is, that everything is happening out of order since the code isn't waiting for the callbacks to finish. I've been using a sleep but that doesn't solve the problem either since it's still jumbled.
I've looked into async, but these functions are intertwined and not really separate, so I wasn't sure how it'd work with async either.
Any suggestions to get this code to run sequentially?
Thanks!

I prefer the promise module to q https://www.npmjs.com/package/promise because of its simplicity
var Promises = require('promise');
var promise = new Promises(function (resolve, reject) {
// do some async stuff
if (success) {
resolve(data);
} else {
reject(reason);
}
});
promise.then(function (data) {
// function called when first promise returned
return new Promises(function (resolve, reject) {
// second async stuff
if (success) {
resolve(data);
} else {
reject(reason);
}
});
}, function (reason) {
// error handler
}).then(function (data) {
// second success handler
}, function (reason) {
// second error handler
}).then(function (data) {
// third success handler
}, function (reason) {
// third error handler
});
As you can see, you can continue like this forever. You can also return simple values instead of promises from the async handlers and then these will simply be passed to the then callback.

I rewrote your code so it was a bit easier to read. You have a few choices of what to do if you want to guarantee synchronous execution:
Use the async library. It provides some helper functions that run your code in series, particularly, this: https://github.com/caolan/async#seriestasks-callback
Use promises to avoid making callbacks, and simplify your code APIs. Promises are a new feature in Javascript, although, in my opinion, you might not want to do this right now. There is still poor library support for promises, and it's not possible to use them with a lot of popular libraries :(
Now -- in regards to your program -- there's actually nothing wrong with your code at all right now (assuming you don't have async code in the pseucode blocks). Your code right now will work just fine, and will execute as expected.
I'd recommend using async for your sequential needs at the moment, as it works both server and client side, is essentially guaranteed to work with all popular libraries, and is well used / tested.
Cleaned up code below
User.find({}, function(err, users) {
for (var i = 0; i < users.length; i++) {
Friend.find({'user':curUser._id}, function(err, friends) {
for (var i = 0; i < friends.length; i++) {
// pseudocode
}
console.log("HERE I'm CHECKING " + curUser);
if (curUser.websiteaccount != "None") {
request.post({ url: 'blah', formData: 'blah' }, function(err, httpResponse, body) {
// pseudocode
sendMail(friendResults, curUser);
});
} else {
// pseudocode
sendMail(friendResults, curUser);
}
});
console.log("finished friend");
console.log(friendResults);
sleep.sleep(15);
console.log("finished waiting");
console.log(friendResults);
}
});

First lets go a bit more functional
var users = User.find({});
users.forEach(function (user) {
var friends = Friend.find({
user: user._id
});
friends.forEach(function (friend) {
if (user.websiteaccount !== 'None') {
post(friend, user);
}
sendMail(friend, user);
});
});
Then lets async that
async.waterfall([
async.apply(Users.find, {}),
function (users, cb) {
async.each(users, function (user, cb) {
async.waterfall([
async.apply(Friends.find, { user, user.id}),
function (friends, cb) {
if (user.websiteAccount !== 'None') {
post(friend, user, function (err, data) {
if (err) {
cb(err);
} else {
sendMail(friend, user, cb);
}
});
} else {
sendMail(friend, user, cb);
}
}
], cb);
});
}
], function (err) {
if (err) {
// all the errors in one spot
throw err;
}
console.log('all done');
});
Also, this is you doing a join, SQL is really good at those.

You'll want to look into something called promises. They'll allow you to chain events and run them in order. Here's a nice tutorial on what they are and how to use them http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

You can also take a look at the Async JavaScript library: Async It provides utility functions for ordering the execution of asynchronous functions in JavaScript.

Note: I think the number of queries you are doing within a handler is a code smell. This problem is probably better solved at the query level. That said, let's proceed!
It's hard to know exactly what you want, because your psuedocode could use a cleanup IMHO, but I'm going to what you want to do is this:
Get all users, and for each user
a. get all the user's friends and for each friend:
send a post request if the user has a website account
send an email
Do something after the process has finished
You can do this many different ways. Vanilla callbacks or async work great; I'm going to advocate for promises because they are the future, and library support is quite good. I'll use rsvp, because it is light, but any Promise/A+ compliant library will do the trick.
// helpers to simulate async calls
var User = {}, Friend = {}, request = {};
var asyncTask = User.find = Friend.find = request.post = function (cb) {
setTimeout(function () {
var result = [1, 2, 3];
cb(null, result);
}, 10);
};
User.find(function (err, usersResults) {
// we reduce over the results, creating a "chain" of promises
// that we can .then off of
var userTask = usersResults.reduce(function (outerChain, outerResult) {
return outerChain.then(function (outerValue) {
// since we do not care about the return value or order
// of the asynchronous calls here, we just nest them
// and resolve our promise when they are done
return new RSVP.Promise(function (resolveFriend, reject){
Friend.find(function (err, friendResults) {
friendResults.forEach(function (result) {
request.post(function(err, finalResult) {
resolveFriend(outerValue + '\n finished user' + outerResult);
}, true);
});
});
});
});
}, RSVP.Promise.resolve(''));
// handle success
userTask.then(function (res) {
document.body.textContent = res;
});
// handle errors
userTask.catch(function (err) {
console.log(error);
});
});
jsbin

Related

Pass variables between Promises and functions

I have a problem.
I have to do two different SOAP calls to retrieve two list of vouchers and then use these lists to do a check on them and to do some job.
I put the two calls in different Promise functions because I want start the job on the lists after the call returned its result.
This is the first Promise call:
let vouchers = function(voucherTypeList){
return new Promise(function(resolve,reject){
const categoryId = "1000";
let args = {
"tns:CategoryId": categoryId
};
var header = {
"tns:Authenticate": {
"tns:UserName": soapVoucherWsdlUsername,
"tns:Password": soapVoucherWsdlPassword
}
};
// let voucherTypeList;
voucherClient.addSoapHeader(header);
voucherClient.GetVouchers(args, function(err, result) {
console.log("DENTRO GET VOUCHERS");
if (err) {
console.log(err);
writeResponse(res, '200', err);
} else {
//++++++++++++++++++++++
//voucherTypeList is what I want to return to the main function
voucherTypeList = mapGetVoucherTypeListResponse(result);
//++++++++++++++++++++++
}
resolve("done 1");
});
});
}
This is the second Promise call:
let issuedVouchers = function(accountId) {
return new Promise(function (resolve, reject) {
const categoryId = "1000";
let args = {
"tns:CategoryId": categoryId,
"tns:CheckRedeem": true,
"tns:IncludeRedeemed": false,
"tns:CardId": accountId
};
var header = {
"tns:Authenticate": {
"tns:UserName": soapVoucherWsdlUsername,
"tns:Password": soapVoucherWsdlPassword
}
};
let issuedVoucherList;
voucherClient.addSoapHeader(header);
voucherClient.GetVouchers(args, function (err, result) {
console.log("DENTRO GET ISSUED VOUCHERS");
if (err) {
console.log(err);
writeResponse(res, '200', err);
} else {
//++++++++++++++++++++++
//issuedTypeList is what I want to return to the main function
issuedTypeList = mapGetVoucherTypeListResponse(result);
//++++++++++++++++++++++
}
resolve("done 2");
});
});
}
And this is the main function, with the Promise flow:
function getAvailableVoucherTypes(req, res) {
var accountId = req.params.accountId;
vouchers(voucherTypeList).
then(issuedVouchers(accountId)).
then(function() {
//here I want to use voucherTypeList and issuedTypeList
//and do some jobs on them
console.log("OK");
});
}
How can I do this? I tried many solutions, but I'm not able to see voucherTypeList and issuedTypeList in the main function.
The then callbacks are getting the value of what you pass to the resolve function in your promises. You are currently passing arbitrary strings, which is useless... But for the demonstration, let's keep those and just log their values in your main script:
function getAvailableVoucherTypes(req, res) {
var accountId = req.params.accountId;
vouchers(voucherTypeList).
then(function(result){
console.log(result); //done 1
return issuedVouchers(accountId);
}).
then(function(result) {
console.log(result); //done 2
//here I want to use voucherTypeList and issuedTypeList
//and do some jobs on them
console.log("OK");
});
}
I'll let you play with your promises to pass the right variables...
Now, it seems that your 2 calls do not need to be sequential, so let's make them parallel, it's gonna be slightly easier for us too.
function getAvailableVoucherTypes(req, res) {
var accountId = req.params.accountId;
var promises = [vouchers(),issuedVouchers(accountId)]
Promise.all(promises).then(function(results){
//In Promise.all, the results of each promise are passed as array
//the order is the same as the order of the promises array.
var voucherTypeList = results[0];
var issuedTypeList = results[1];
});
}
BONUS: I do not want to complicate this task too much before you grasp it correctly. So I won't add more code. But note that you should use reject too, instead of handling your errors in every promise, you should reject them when things go wrong. Just reject(err) and add a second callback to your main script's then to handle any error that may happen. If you keep resolving your promises that did not work, you will not be passing the elements you are expecting and you'll need to add checks over every step.
Let's modify the GetVouchers callback to fit what I suggest.
voucherClient.GetVouchers(args, function (err, result) {
console.log("DENTRO GET ISSUED VOUCHERS");
if (err) {
reject(err);
} else {
resolve(mapGetVoucherTypeListResponse(result));
}
});
Once it is done on both your promises, we can change your main script to handle the error accordingly.
Promise.all(promises).then(function(results){
//Handle success like above.
},function(err){
//Handle error.
console.log(err.stack || err);
writeResponse(res, '200', err);
});

how to can i handle multiple callbacks return values in nodejs?

I am trying to perform sql queries based on the callback results in if conditions but i am unable to write the code .so please provide som information in code
app.get('/resell-property', function(req, res) {
var data = {}
data.unit_price_id = 1;
function callback(error, result) {
if (result.count == 0) {
return hp_property_sell_request.create(data)
}
else if (result.count > 0) {
return hp_unit_price.findAll({
where: {
unit_price_id: data.unit_price_id,
hp_property_id: data.property_id,
hp_unit_details_id: data.unit_details_id
}
})
}
}
hp_property_sell_request.findAndCountAll({
where: {
unit_price_id: data.unit_price_id
}
}).then(function (result) {
if (result) {
callback(null, result);
}
});
});
In this how can i write the callbacks for
hp_property_sell_request.create(data) ,hp_unit_price.findAll({
where: {
unit_price_id: data.unit_price_id,
hp_property_id: data.property_id,
hp_unit_details_id: data.unit_details_id
}
})
In that after returning result again i have to handle callbacks and perform this query
if(result.request_id){
return hp_unit_price.findAll({
where:{
unit_price_id:result.unit_price_id,
hp_property_id:result.property_id,
hp_unit_details_id:result.unit_details_id
}
}).then(function (result){
if(result.is_resale_unit==0 && result.sold_out==0){
return Sequelize.query('UPDATE hp_unit_price SET resale_unit_status=1 WHERE hp_unit_details_id='+result.unit_details_id+' and hp_property_id='+result.property_id)
}
})
}
The promise resolve function takes only one input argument, so if you need to pass in multiple stuff, you have to enclose them in a single object. Like, if you have to go with something like:
database.openCollection()
.then(function(collection){
var result = collection.query(something);
var resultObject = { result: result, collection: collection };
})
.then(function(resultObject){
doSomethingSyncronousWithResult(resultObject.result);
resultObject.collection.close();
});
You can't use Promise all if all of your stuff isn't a result of a promise resolve, you might need to go with something like this.
Disclaimer: The code example is a very poor one, but it explains the concept.
I would suggest you to learn about Promises, particularly Bluebird.
You can promisify traditional callback methods.
I would also create model level functions in different files. Here's an example.
parent.js
const db = require("./connections/database"); // connection to database
const getChildForParent = function (parentId, childId, callback) {
db.find({parent: parentId, child_id: childId}, "childrenTable", function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
};
children.js
const db = require("./connections/database"); // connection to database
const getToysForChild = function (childId, callback) {
db.find({toy_belongs_to: parentId}, "toysTable", function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
};
Then in controller you can do something like this:
const Bluebird = require("bluebird");
const Parent = require("./parent.js");
const Child = require("./child.js");
// Promisifying adds "Async" at the end of your methods' names (these are promisified)
Bluebird.promisifyAll(Parent);
Bluebird.promisifyAll(Child);
// Just an example.
app.get("/parent/:parentId/children/:childId", function(req, res) {
return Bluebird.try(function() {
return User.getChildForParentAsync(req.params.parentId, req.params.childId);
}).then(function(child) {
return Child.getToysForChildAsync(child.child_id);
}).then(function(toys) {
// Do something with toys.
});
});
Of course you can do much more with this and this is not the only way.
Also you can use Promise.all(). This method is useful for when you want to wait for more than one promise to complete.
Let's say you have a list of urls that you want to fetch and process the results after all the data has been fetched.
var urls = [url1, url2, url3, url4, url5 .......... ];
var Bluebird = require("bluebird");
var request = require("request"); // callback version library
Bluebird.promisifyAll(request);
// create a list which will keep all the promises
var promises = [];
urls.forEach(function(url) {
promises.push(request.getAsync(url1));
});
// promises array has all the promises
// Then define what you want to do on completion.
Bluebird.all(promises).then(function(results) {
// results is an array with result a url in an index
// process results.
});
I would recommend to use Promises to solve that. If you need all results of all Requests, when they are all done Promise.all() will do that for you. Your basic could look like that:
var req1 = new Promise(function(res, rej){
var req = new XMLHttpRequest()
…
req.addEventListener('load', function (e) {
res(e);
})
var req2 = //similar to the above
Promise.all([req1, req2, …]).then(function(values){
//all requests are done here and you can do your stuff
});
You can also use the new fetch api, which creates Promises like so:
var req1 = fetch(…);
var req2 = fetch(…);
Promise.all([req1, re2, …]).then(…);

Asynchronously Write Large Array of Objects to Redis with Node.js

I created a Node.js script that creates a large array of randomly generated test data and I want to write it to a Redis DB. I am using the redis client library and the async library. Initially, I tried executing a redisClient.hset(...) command within the for loop that generates my test data, but after some Googling, I learned the Redis method is asynchronous while the for loop is synchronous. After seeing some questions on StackOverflow, I can't get it to work the way I want.
I can write to Redis without a problem with a small array or larger, such as one with 100,000 items. However, it does not work well when I have an array of 5,000,000 items. I end up not having enough memory because the redis commands seem to be queueing up, but aren't executed until after async.each(...) is complete and the node process does not exit. How do I get the Redis client to actually execute the commands, as I call redisClient.hset(...)?
Here a fragment of the code I am working with.
var redis = require('redis');
var async = require('async');
var redisClient = redis.createClient(6379, '192.168.1.150');
var testData = generateTestData();
async.each(testData, function(item, callback) {
var someData = JSON.stringify(item.data);
redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
console.log("Item was persisted. Result: " +reply);
});
callback();
}, function(err) {
if (err) {
console.error(err);
} else {
console.log.info("Items have been persisted to Redis.");
}
});
You could call eachLimit to ensure you are not executing too many redisClient.hset calls at the same time.
To avoid overflowing the call stack you could do setTimeout(callback, 0); instead of calling the callback directly.
edit:
Forget what I said about setTimeout. All you need to do is call the callback at the right place. Like so:
redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
console.log("Item was persisted. Result: " +reply);
callback();
});
You may still want to use eachLimit and try out which limit works best.
By the way - async.each is supposed to be used only on code that schedules the invocation of the callback in the javascript event queue (e.g. timer, network, etc) . Never use it on code that calls the callback immediately as was the case in your original code.
edit:
You can implement your own eachLimit function that instead of an array takes a generator as it's first argument. Then you write a generator function to create the test data. For that to work, node needs to be run with "node --harmony code.js".
function eachLimit(generator, limit, iterator, callback) {
var isError = false, j;
function startNextSetOfActions() {
var elems = [];
for(var i = 0; i < limit; i++) {
j = generator.next();
if(j.done) break;
elems.push(j.value);
}
var activeActions = elems.length;
if(activeActions === 0) {
callback(null);
}
elems.forEach(function(elem) {
iterator(elem, function(err) {
if(isError) return;
else if(err) {
callback(err);
isError = true;
return;
}
activeActions--;
if(activeActions === 0) startNextSetOfActions();
});
});
}
startNextSetOfActions();
}
function* testData() {
while(...) {
yield new Data(...);
}
}
eachLimit(testData(), 10, function(item, callback) {
var someData = JSON.stringify(item.data);
redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
if(err) callback(err);
else {
console.log("Item was persisted. Result: " +reply);
callback();
}
});
}, function(err) {
if (err) {
console.error(err);
} else {
console.log.info("Items have been persisted to Redis.");
}
});

setTimeout in nodejs - for loops

I have looked over this post: setTimeout in Node.js loop in hopes that it would solve my issue, and in a way it has. However, I am encountering a new issue that I am unable to resolve on my own.
I have been trying to fix this issue for the past couple of hours, but I have had no luck. Here is what I have:
The function that needs to be called in the timeout function:
function searchLDAP(i, app, userUID){
app.process_args(userUID[i]);
}
This is the portion of the code that is not working properly. The code works for the first iteration (userUID[0]), however when it tries to recurse, i becomes undefined.
function doSetTimeout(i, count, app, userUID) {
if(i == count - 1){ callback(); }
searchLDAP(i, app, userUID);
++i;
setTimeout(doSetTimeout, 2000);
}
I am using node's async module
async.series([
function(callback) {
app.readLines(input, callback); // userUID is and array that
// is returned from this function
},
function() {
var count = userUID.length;
var i = 0;
doSetTimeout(i, count, app, userUID);
}
], function(err) {
console.log('all functions complete');
});
Thank you in advance
-Patrick
With setTimeout, you only command which function should be called, but do not pass the set of arguments that the function should use. And here's one possible approach to solve this problem:
function doSetTimeout(i, count, app, userUID) {
if (i == count - 1) {
return; // you should stop the recursion
}
searchLDAP(i, app, userUID);
setTimeout(function() {
doSetTimeout(i + 1, count, app, userUID);
}, 2000);
}
As an alternative to raina77ow's answer, you can also better use async (and dramatically reduce complexity and lines of code) like this:
app.readLines(input, function(err, users) {
async.each(users, function(user, next) {
app.process_args(user);
next();
}, function(err) {
console.log('all functions complete');
});
});
This will iterate over each of your users (the userUID array from you post), and pass the value to the function. No need to mess with tracking the value of i yourself. This isn't rate limited and doens't have the setTimeout which is only necessary if your process_args method cannot handle concurrent calls. If you need make sure that there is only one app.process_args being called at a time, you can use eachSeries instead:
app.readLines(input, function(err, users) {
async.eachSeries(users, function(user, next) {
app.process_args(user);
next();
}, function(err) {
console.log('all functions complete');
});
});
And if for some reason you really need a two second delay between calls, you can do the following:
app.readLines(input, function(err, users) {
async.eachSeries(users, function(user, next) {
setTimeout(function() {
app.process_args(user);
next();
}, 2000);
}, function(err) {
console.log('all functions complete');
});
});

a way to know when all callbacks are done in javascript

I have many calls to a service at the end of which i want to write to a file my final collection when all the callbacks of the service have returned.
is there there a way to be sure that all callbacks are done ?
for (id in idsCollection) {
object.callService(id, function (res) {
collection.push(res);
});
}
filewriter.writetoFile("filename.json", JSon.Stringify(collection));
EDIT : just for the record i'm using cheerio with nodeJS.
Create an array. Push something onto the array each time you set up a callback. Pop something off it each time the callback runs. Check to see if the array is empty inside the callback function. If it is empty, then all the callbacks are done.
I typically use the node-async library for this sort of thing. It makes it easy to do exactly what you're talking about:
async.each(yourArray,
function(element, next) {
// this callback gets called for each element in your array
element.doSomething(function(returnValue){
next(returnValue) // call next when you're done
}
}, function(err, returnValues) {
// when all the elements in the array are processed, this is called
if (err) return console.log(err);
console.log(returnValues) // this is an array of the returnValues
});
})
You could simply count them. In your case it seems you already know how many callbacks there are going to be.
var remaining = idsCollection.length; // assuming array
for (id in idsCollection) {
object.callService(id, function (res) {
collection.push(res);
remaining -= 1; // decrement by 1 per callback
// here you can check if remaining === 0 (all done)
});
}
you can use nimble lib http://caolan.github.io/nimble/.
nimble paralel example
var _ = require('nimble');
_.parallel([
function (callback) {
setTimeout(function () {
console.log('one');
callback();
}, 25);
},
function (callback) {
setTimeout(function () {
console.log('two');
callback();
}, 0);
}
], function(){
console.log('done')
});
output
> two
> one
> done
I see many answers here, but I hope that this solution may still help someone.
Create a promise for each callback to be extinguished as such:
function funcToLoop(arg){
return new Promise((resolve, reject) => {
try{
funcWithCallback(arg, (cbArg) => {
// do your stuff
resolve(cbArg)
});
} catch (e) {
reject(e)
}
});
}
Then, you can create a loop as a async function and handle eventual results/states/etc here:
async function mainLoop(array){
let results = [];
for (let arg of array){
results.push(await funcToLoop(arg))
}
// handle results
}
... or you can have a sync function, collect the promises and handle them:
function mainLoop(array){
let promises = [];
for (let arg of array){
promises.push(funcToLoop(arg))
}
Promise.all(promises).then(()=>{
// handle promises
})
}
Claudio
jQuery.Deferred() objects might be what you are looking for.
OR if you are using HTML5 you can use promises .
Here is how to create promises
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
And here is how to use them
promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
Check this link for more info
If you're using jQuery, you can use $.when
Example:
exmCall1 = $.getJson(..);
exmCall2 = $.getJson(..);
$.when(exmCall1, exmCall2).done(function (exmCall1Ret, exmCall2Ret) {
//do stuff
});
You can read the actual documentation here: http://api.jquery.com/jquery.when/
Or do some hardcode:
var running;
for (id in idsCollection) {
object.callService(id, function (res) {
collection.push(res);
running += 1;
});
}
var loop = setInterval(function() {
if(running >= idsCollection.length) {
filewriter.writetoFile("filename.json", JSon.Stringify(collection));
clearInterval(loop);
}
, 500);

Categories

Resources