Promises not working as expected with async.each - node.js

I'm somehow not able to find the solution. Here is what I'm doing:
async.each(bottles, function(bottle) {
// set substance quantity to 0
updateQuantity(bottle[constant.SUBSTANCE_ID], 0)
.then(function() {
bottle[constant.EXPIRY] = expiry;
bottle[constant.IS_AVAILABLE] = true;
updateRecord(bottle)
.then(function() {
console.log('Updated');
},
function() {});
}, function() {});
}, function() {
console.log('Finished');
});
console.log('Done');
The method updateQuantity and updateRecord returns promises and are using Knex update in the background which also returns a promise.
I want the output of the above code to be:
Updated
Updated
Finished
Done
But the output I'm getting is:
Done
Updated
Updated
So, the callback of async.each is not working and the code is not waiting for async.each to finish.

You have to call the passed in callback from the async.each operation. As it is, async doesnt know when your callback resolves.
Also, DONE should always be the first text you see as async.each doesn't block. When you start running running the script, async.each will be registered, but because your code is asynchronous it will reach the console.log('Done') before the updateRecord and updateQuantities complete
async.each(bottles, function(bottle, callback) {
updateQuantity(bottle[constant.SUBSTANCE_ID], 0)
.then(function() {
...
updateRecord(bottle)
.then(function() {
console.log('Updated');
callback();
},
function() {});
}, function() {});
}, function() {
console.log('Finished');
});
console.log('Done');

Don't use async.js at all with promises, they are capable of what you need with their builtin methods and you don't need to fall back to callbacks.
bottles.reduce(function(prev, bottle) {
return prev.then(function() {
// set substance quantity to 0
return updateQuantity(bottle[constant.SUBSTANCE_ID], 0)
}).then(function() {
bottle[constant.EXPIRY] = expiry;
bottle[constant.IS_AVAILABLE] = true;
return updateRecord(bottle);
}).then(function() {
console.log('Updated');
});
}, Promise.resolve()).then(function() {
console.log('Finished successfully');
}, function(err) {
console.error(err)
}).then(function() {
console.log('Done'); // notice you must put this in the callback
});
console.log('Started');

Related

Hard time uderstaning async.parallel in node

I have this function in the controller
router.post('/', function(req, res, next) {
if (req.user.isPremium == false) {
// Free user - Single report
let website = req.body.website0;
let builtWithCall = `https://api.builtwith.com/free1/api.json?KEY=APIKEY&LOOKUP=${website}`;
let pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=APIKEY`;
// curl 'https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://georgiancollege.ca&strategy=mobile&key=APIKEY'
var calls = [];
calls.push(function(callback) {
// First call
https.get(builtWithCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log('BuiltWith received', data);
});
});
});
calls.push(function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
});
} else {
// Premium user - comparison report
let websites = [];
}
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
});
I am trying to run several async API calls at once. The problem is, when I try to run them like this
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
the console doesn't log anything.
When I do the console log here though
function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
}
it logs the response. The pageSpeed one gets in a weird loop and repeats itself multiple times, but at least it works.
Now what am I doing wrong with the async.parallel part? Also what is this callback in function(callback) {?
EDIT:
This is the new version of the anonymous function:
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end', function(data) {
callback(null, data);
});
resource.on('error', function(err) {
callback(err);
});
});
}
You need to call the passed in callback. Looking at your one parallel function you are not calling callback(). I'll assume your resource object has an end & error
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end' function() {
callback(null, results);
});
resource.on('error' function(err) {
callback(err);
});
});
}
How async.parallel works is all the functions called must in turn call the passed in callback function; in your case that is callback.
Once each function in the parallel calls callback then and only then will the final function be called, which is the function you defined as function(err, results) {...}.
There is one caveat, if in the callback call you pass non-null for the first argument then that final function will be called immediately where you should handle that error if it happens.

nodejs mongodb async.Each done

How to detect when async.Each is done loop all objects/arrays loaded into the parameters. how to call the ALL FINISH log.
db.collection('tests').find(query).limit(10).toArray(function(err, db_results) {
console.log("count: "+db_results.length);
async.each(db_results, function (db_resultsx, cb) {
db_resultsx._id = db_resultsx._id.toString();
db_resultsx.xdate_visited = moment(db_resultsx.date_visited).format("YYYY-MM-DD hh:mm:ss");
documents.push(db_resultsx);
}, function(documents) {
console.log(documents);
console.log(documents.length);
console.log("ALL FINISH");
process.exit();
});
});
The reason that the async.each function is not finishing is that the callback isn't being called within the second function. I've created a snipped with a simple example without the mongo call but with a timeout instead.
If you look at the docs https://caolan.github.io/async/docs.html#each the second argument is a callback (in your code this is the value cb) that needs to be called to signify the end of the code you are running.
function delayedReturn(callback) {
setTimeout(function(){
console.log('timeout complete');
callback(false, ['first value']);
}, 1000);
}
var documents = [];
delayedReturn(function(err, db_results) {
async.each(db_results, function (dbResult, cb) {
documents.push(dbResult);
console.log('documents pushed', dbResult)
cb();
}, function(error) {
if(error)
{
console.error(error);
}
});
});
<script src="https://rawgit.com/caolan/async/master/dist/async.min.js"></script>
There's no reason to use async.each() given that the code that it should be running isn't asynchronous.
Instead, you can use .map():
db.collection("tests").find(query).limit(10).toArray(function(err, db_results) {
console.log("count: " + db_results.length);
let documents = db_results.map(function(db_resultsx) {
db_resultsx._id = db_resultsx._id.toString();
db_resultsx.xdate_visited = moment(db_resultsx.date_visited).format(
"YYYY-MM-DD hh:mm:ss"
);
return db_resultsx;
});
console.log(documents);
console.log(documents.length);
curl(URL, { data : documents }, function(err) {
console.log("ALL FINISH");
process.exit();
});
});

Nodejs run task in sequence

I am new to node.js and I just don't know how to execute a settimeout function before another function,
for example,
var async = require('async');
function hello(){
setTimeout(function(){
console.log('hello');
},2000);}
function world(){
console.log("world");
}
async.series([hello,world()]);
and the output is always world hello.
Am I using the library right? I dont the question seems trivial but I really have no idea how to force a short task to run after a long one
Async requires you to use callback. Follow this link to see some examples. The following code should output hello world correctly:
var async = require("async");
function hello(callback) {
setTimeout(function(){
console.log('hello');
callback();
}, 2000);
}
function world(callback) {
console.log("world");
callback();
}
async.series([hello, world], function (err, results) {
// results is an array of the value returned from each function
// Handling errors here
if (err) {
console.log(err);
}
});
Note that callback() was called inside the setTimeout() function so that it waits for the console.log('hello').
Use promise
function hello(){
return new Promise(function(resolve, reject) {
console.log('hello');
resolve();
});
}
function world(){
return new Promise(function(resolve, reject) {
console.log("world");
resolve();
});
}
hello()
.then(function(){
return world()
})
.then(function(){
console.log('both done');
})
.catch(function(err){
console.log(err);
});
You can use promises instead of callbacks. so your code will be something like below:
var async = require('async');
function hello(){
setTimeout(function(){
console.log('hello');
},2000);}
function world(){
console.log("world");
}
return Q.fcall(function() {
hello();
})
.then(function(resultOfHelloFunction){
world();
});
The World() function will only be executed when hello() function is completed its execution.
P.S : I am using Q library to promisify functions. It's totally fine to use other libraries (such as bluebird) to achieve the same thing.
You can use callback which are the heart of the nodejs.
var fun1 = function(cb){
// long task
// on done
return cb(null, result);
}
var fun2 = function(cb){
return cb(null, data);
}
fun1(function(err, result){
if(!err){
fun2(function(er, data){
// do here last task
})
}
}
// to avoid pyramid of doom use native promises
func1 (){
return new Promise((resolve, reject) => {
// do stuff here
resolve(data)
})
}
func2(){
return new Promise((resolve, reject) => {
resolve(data);
})
}
And then call them using:
func1.then((data) => {
return func2();
})
.then((resule) => {
//do here stuff when both promises are resolved
})

How to perform async.each in async .series

In my function i have to call async series inside async foreach to compute the final result and create my json.is that possible
async.series([
function(callback) {
});
},
function(callback) {
async.forEachSeries(temp,function(quest,callback) {
}, function(err) {
if (err) return next(err);
});
callback();
}
],
function(err) {
if (err) return next(err);
res.json(output);
});
You should be able to nest as much async functions into each other, however it is better to use a naming conventions so you can easily track which callbacks are passed where and to avoid collisions due to hoisting. So basically this should work as you'd expect:
async.series([
function first(seriesCallback) {
seriesCallback();
},
// other functions in series
// ...
function (seriesCallback) {
var someArray = [];
async.each(someArray, function (value, eachCallback) {
// process the value
// return an error if there is one
eachCallback(err);
}, function(err) {
// add any additional processing you might need
// pass the control to the parent async method and handle the errors
// in a more central place if there is an error here it will
// be processed in onSeriesDone as well as all other errors
seriesCallback(err);
});
}], function onSeriesDone(err) {
next(err);
});

Callback not getting called while using async.series

I have the following code, where i am calling a callback after my async task is completed:
var async = require("async");
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(_callback) {
console.log('Getting some data');
_callback(null, result);
});
tasks.push(function(_callback) {
console.log('Second function called');
_callback(null, result);
});
async.series(tasks, function(){
console.log('All done');
});
}
tasks();
I have checked against the syntax expected in the async library. Code looks similar to me.
Can someone point out whats needs to be changed here.
Try it like this:
var async = require("async");
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(next) {
console.log('Getting some data');
next(null, result);
});
tasks.push(function(_callback) {
console.log('Second function called');
next(null, result);
});
async.series(tasks, function(err, result){
console.log('All done');
_callback(err, result);
});
}
tasks();
Async gives every single task a callback which you need to call to let async now that this task finished.
After all tasks are finished you can call your callback.
If you want your callback to get called after every single task is finished just add before every
next(null, result) also an _callback(null, result)
The reason that wouldn't work on your implementation is that you overwrite the global _callback method with the argument that has the same name.
Hope that makes sense :)
As it stands in your code right now, the _callback function you define above should never get called, as the _callback in the tasks functions is scope to the one passed by async.
// this will never get called
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
tasks.push(function(_callback) { // the _callback here overrides the one above for this closure
console.log('Getting some data');
_callback(null, result);
});
// ... more tasks
async.series(tasks, function(){
console.log('All done');
});
}
If you want to reuse the logic in your _callback function, I'd recommend passing it the async callback as a parameter:
// rename some functions to avoid silly js scoping mysteries
function myCallback(err, result, asyncCallback){
// shared logic here
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
asyncCallback(null, result);
}
// ...
tasks.push(function(asyncCallback) { // the _callback here overrides the one above for this closure
console.log('Getting some data');
myCallback(null, result, asyncCallback);
});
// ... more tasks
async wants its callback to be called so it knows when to continue, but that doesn't mean you can't intercept it with your own handlers :P.
You don't need the function call to _callback. You could simplify it like this.
var async = require("async");
function doSomeLogic(item, callback) {
console.log(item);
callback(null, item + 2);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(callback){
// do some more stuff ...
callback(null, 'one');
});
tasks.push(function(callback){
// do some more stuff ...
callback(null, 'two');
});
async.series(tasks, function(error, results) {
if (error) {
console.log(error);
}
console.log(results);
console.log('All done');
});
}
tasks();

Resources