Multiple Async series causing wrong behavior in Javascript - javascript

I'm using async library(the browser version) . I am stuck at the point where I see a fail in the normal(read 'expected') behavior of async series when i nest async calls inside one another.
Here are some fiddles producing wrong outputs
fiddle 1
fiddle 2
The problem is summarized here
With the code as below
$(function () {
async.series([
function f1(cb) {
setTimeout(function () {
console.log("Hello from f1");
cb(null);
}, 3000);
async.series([
function f11(cb) {
setTimeout(function () {
console.log("Hello from f11");
cb(null);
}, 3000);
}, function f12(cb) {
setTimeout(function () {
console.log("Hello from f12");
cb(null);
}, 3000);
}, function f13(cb) {
setTimeout(function () {
console.log("Hello from f13");
cb(null);
}, 3000);
}], function (err, res) {
console.log("Done all in f1");
});
}, function f2(cb) {
setTimeout(function () {
console.log("Hello from f2");
cb(null);
}, 3000);
}],
function (err, res) {
console.log("Done all");
});
});
I expected the output as below
Hello from f1
Hello from f11
Hello from f12
Hello from f13
Done all in f1
Hello from f2
Done all
But I am getting this -
Hello from f1
Hello from f11
Hello from f2
Done all
Hello from f12
Hello from f13
Done all in f1
Do you see the problem as I see? I think async.series gets confused because of multiple async series running at once. How can I pipe the nested ones into the main one , if that is the case. Anyone help #Caolan
The answer as per EDITs from #tom. It was a mistake to call cb(null) early in the code -- corrected code below
$(function () {
async.series([
function f1(cb) {
setTimeout(function () {
console.log("Hello from f1");
}, 3000);
async.series([
function f11(cb1) {
setTimeout(function () {
console.log("Hello from f11");
cb1(null);
}, 3000);
}, function f12(cb1) {
setTimeout(function () {
console.log("Hello from f12");
cb1(null);
}, 3000);
}, function f13(cb1) {
setTimeout(function () {
console.log("Hello from f13");
cb1(null);
}, 3000);
}], function (err, res) {
console.log("Done all in f1");
cb(null);
});
}, function f2(cb) {
setTimeout(function () {
console.log("Hello from f2");
cb(null);
}, 3000);
}],
function (err, res) {
console.log("Done all");
});
});

With asynchronous functions, invoking the callback is analogous to returning from a normal function in the sense that it resumes execution of the rest of the program. In function f1, invoking cb causes f2 to be executed (which eventually prints Hello from f2 and Done all).
To make sure f2 is executed after f11, f12 and f13 have all finished, the call to cb should be moved to the final callback of the inner async.series() call, after the line console.log("Done all in f1").

The answer as per EDITs from #tom. It was a mistake to call cb(null) early in the code -- corrected code has been edited above in the question itself.

Related

Why does the script embedded in the callback run first in this example?

I would like to understand why does the alert for script2 popup first in this specific case? If I uncomment the setTimeout within the base function declaration, script1 alerts first.
I was expecting that with or without the setTimeout, within the base function, script1 would alert first. A clarifying answer would be much appreciated.
function base(src, cb) {
//setTimeout(() => {
cb();
alert(src + ' finished executing')
//}, 100)
}
function first() {
setTimeout(() => {
alert('first');
}, 110)
}
function second() {
setTimeout(() => {
alert('second');
}, 110);
}
base('script 1', () => {
first();
base('script 2', () => {
second();
});
})
The function first() is executed before second() but the alert is generated after executing both because you have encapsulated both into the callback method. And the alert is generated after completing the whole function. Maybe you should write your code in following way to understand what happens.
function base(src, cb) {
//setTimeout(() => {
cb();
alert(src + ' finished executing')
//}, 100)
}
function first() {
setTimeout(() => {
alert('first');
}, 110)
}
function second() {
setTimeout(() => {
alert('second');
}, 110);
}
base('script 1 and script 2', () => {
base('script 1', () => {
first();
});
base('script 2', () => {
second();
});
})
Your code invokes the base() function, passing in a function that also calls base(). Thus the "second" function finishes first, because the nested call to base() invokes it before it posts the alert() for the "first" function.
Use console.log() instead of alert().

Calling timeout out with callback

function fn1() {
setTimeout(() => {
console.log('hello');
}, 5000);
}
function fn2() {
setTimeout(() => {
console.log('goodbye');
}, 3000);
}
a) How to modify above functions using callback so that it can print first "hello" and then "goodbye".
b) i wanted to then print "goodbye" then "hello" and after that just wanted to print "completed".how to do that this as well through callback.
I'm not sure I understood your question properly but is this more of what you're looking for?
function fn1(callback) {
setTimeout(() => {
console.log('hello');
}, 2999);
callback();
}
function fn2() {
setTimeout(() => {
console.log('goodbye');
}, 3000);
}
fn1(function() {
fn2();
setTimeout(function() {
console.log("completed");
}, 3001)
});
if so then you have to set the timeout value properly, if you want them to print in that order remember that the values should be sequential too, otherwise you'd have to set new timeouts for fn2 and the following console.log('Completed'), like this:
function fn1(callback) {
setTimeout(() => {
console.log('hello');
}, 5000);
callback();
}
function fn2() {
setTimeout(() => {
console.log('goodbye');
}, 3000);
}
fn1(function() { // callback
setTimeout(function() { // timeout so fn2 fires once fn1 delay is finished
fn2();
setTimeout(function() {
// add your console.log("completed"); or third function
// here so it fires right after fn2
}, 3001); // 3000 fn2 timeout + 1ms more so it fires right after
}, 2001); // the difference between fn1 timeout - fn2 timeout + 1ms so it fires right after
});
Notice that, when you pass a callback into another function, you only pass the reference to the function (without executing it, thus without the parenthesis ()).
And on the return we check if the reference to the function was passed (better to check with lodash or _ isFunction method because it may cause an error if foo is not a function)
const fn1 = (foo = false) => {
setTimeout(console.log('goodbye'), 300);
return foo && foo()
}
const fn2 = (foo = false) => {
setTimeout( console.log('hello'), 500);
return foo && foo()
}
fn1(fn2)
fn2(fn1)

async.auto: stop entire chain after first error

I was under the impression that the behavior of async.auto was that if one of the tasks returned an err, the global callback would be called with this error and all subsequent tasks would not execute. After all, why would they? The global callback has been called so the error has been reported already.
It turns out that only tasks that depend on the faulty task will not run, and the rest will.
'use strict';
var async = require('async');
async.auto({
downloadAudio: function (callback) {
console.log("downloading audio...");
setTimeout(function () {
console.log("downloaded audio.");
callback();
}, 10000);
},
downloadScript: function (callback) {
console.log("downloading script...");
setTimeout(function () {
return callback(new Error("ERROR downloading script!"));
}, 1000);
},
preprocessAudio: ['downloadAudio', function (callback) {
console.log("preprocessing audio...");
setTimeout(function () {
console.log("done preprocessing audio.");
callback();
}, 5000);
}]
}, function (err) {
if (err) {
return console.error(err.message);
}
console.log('done.');
});
In this code snippet, both downloadAudio and downloadScript will immediately run in parallel, and preprocessAudio depends on downloadAudio. The problem is that when downloadScript returns an err, preprocessAudio will still run, even though there's no point because the thing failed as a whole already:
downloading audio...
downloading script...
ERROR downloading script!
downloaded audio.
preprocessing audio...
done preprocessing audio.
I know, I could make preprocessAudio depend on downloadScript also but that means that preprocessAudio would wait for downloadScript to complete, even though they could run in parallel without a problem.
Is there any elegant way to solve this?
Thanks,
Jan
Given then that you don't want preprocessaudio to run if downloadScript has returned an error, preprocessaudio really does 'rely' on the other two functions.
Just add the dependency and all should work correctly.
'use strict';
var async = require('async');
async.auto({
downloadAudio: function (callback) {
console.log("downloading audio...");
setTimeout(function () {
console.log("downloaded audio.");
callback();
}, 10000);
},
downloadScript: function (callback) {
console.log("downloading script...");
setTimeout(function () {
return callback(new Error("ERROR downloading script!"));
}, 1000);
},
preprocessAudio: ['downloadAudio', 'downloadScript', function (callback) {
console.log("preprocessing audio...");
setTimeout(function () {
console.log("done preprocessing audio.");
callback();
}, 5000);
}]
}, function (err) {
if (err) {
return console.error(err.message);
}
console.log('done.');
});

Use Async module with parallel function in Node.js

I'm working with socials API and I need to be able to run those function that return the json from the socials API endpoint in parallel, in a way that only when those function are all finished I can work on their data together.
I'm trying to work with ASync module for npm but I'm not able to understand how to make it works.
Looking at the documentation I've seen this so far:
async.parallel([
function(){ ... },
function(){ ... }
], callback);
The problems is that I didn't understand how can I use it.
I would like to do something like this below, but without timeout and with my async functions ( these function returns me the response from the APIs ).
async.series([
function(callback) {
setTimeout(function() {
console.log(“Task 1”);
callback(null, 1);
}, 300);
},
function(callback) {
setTimeout(function() {
console.log(“Task 2”);
callback(null, 2);
}, 200);
},
function(callback) {
setTimeout(function() {
console.log(“Task 3”);
callback(null, 3);
}, 100);
}
], function(error, results) {
console.log(results);
});
Cuold you please help me ?

node.js async.series is that how it is supposed to work?

var async = require('async');
function callbackhandler(err, results) {
console.log('It came back with this ' + results);
}
function takes5Seconds(callback) {
console.log('Starting 5 second task');
setTimeout( function() {
console.log('Just finshed 5 seconds');
callback(null, 'five');
}, 5000);
}
function takes2Seconds(callback) {
console.log('Starting 2 second task');
setTimeout( function() {
console.log('Just finshed 2 seconds');
callback(null, 'two');
}, 2000);
}
async.series([takes2Seconds(callbackhandler),
takes5Seconds(callbackhandler)], function(err, results){
console.log('Result of the whole run is ' + results);
})
The output looks like below :
Starting 2 second task
Starting 5 second task
Just finshed 2 seconds
It came back with this two
Just finshed 5 seconds
It came back with this five
I was expecting the takes2Second function to finish completely before the takes5Second starts.
Is that how it is supposed to work. Please let me know.
And the final function never runs. Thanks.
Not quite. You are executing the functions immediately (as soon as the array is evaluated), which is why they appear to start at the same time.
The callback passed to each of the functions to be executed is internal to the async library. You execute it once your function has completed, passing an error and/or a value. You don't need to define that function yourself.
The final function never runs in your case because the callback function that async needs you to invoke to move on to the next function in the series never actually gets executed (only your callbackHandler function gets executed).
Try this instead:
async.series([
takes2Seconds,
takes5seconds
], function (err, results) {
// Here, results is an array of the value from each function
console.log(results); // outputs: ['two', 'five']
});
James gave you a good overview of async.series. Note that you can setup anonymous functions in the series array and then call your actual functions with parameters
var async = require('async')
var param1 = 'foobar'
function withParams(param1, callback) {
console.log('withParams function called')
console.log(param1)
callback()
}
function withoutParams(callback) {
console.log('withoutParams function called')
callback()
}
async.series([
function(callback) {
withParams(param1, callback)
},
withoutParams
], function(err) {
console.log('all functions complete')
})
My preferred way to create the async series is using operational array as follow;
var async = require('async'),
operations = [];
operations.push(takes2Seconds);
operations.push(takes5seconds);
async.series(operations, function (err, results) {
// results[1]
// results[2]
});
function takes2Seconds(callback) {
callback(null, results);
}
function takes5seconds(callback) {
callback(null, results);
}
async.series
([
function (callback)
{
response=wsCall.post(user,url,method,response);
console.log("one");
callback();
}
,
function (callback)
{
console.log("two");
//console.log(response);
callback();
}
]
,
function(err)
{
console.log('Both a and b are saved now');
console.log(response);
});
In async.series,all the functions are executed in series and the consolidated outputs of each function is passed to the final callback. e.g
var async = require('async');
async.series([
function (callback) {
console.log('First Execute..');
callback(null, 'userPersonalData');
},
function (callback) {
console.log('Second Execute.. ');
callback(null, 'userDependentData');
}
],
function (err, result) {
console.log(result);
});
Output:
First Execute..
Second Execute..
['userPersonalData','userDependentData'] //result

Categories

Resources