async.auto: stop entire chain after first error - javascript

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.');
});

Related

Stoppable sequence of async tasks

What would be the best way to implement a stoppable list of asynchronous tasks ?
The number of tasks aren't fixed.
I've found a way but I'm wondering if there is a better way to implement such thing. Also the code looks rather dirty, which makes me think that that's probably not the way to go.
The main issues are:
Detect when the end of tasks is requested.
Be able to execute a chain of asynchronous functions, with the possibility of quitting the process at one point, regardless of the remaining tasks.
For issue (2), I found a decent way, which is by using async.waterfall. Sending a value to the error parameter of the callback causes all the processes to stop.
Now comes the dirty part, for (1). To achieve this, each task has to listen for a value change in a object (let's stay, the state of the sequence) in order to detect when quitting the process is required.
So, a prototype of the implementation looks like that:
let sequenceState = new Proxy(
{
stopped: false,
onStop: () => { }
},
{
set: function (target, prop, value) {
if (prop === "stopped") {
target.stopped = value;
if (value)
target.onStop();
} else {
target[prop] = value;
}
}
}
);
function stop() {
sequenceState.stopped = true;
}
function task1(callback) {
console.log("Task 1...");
let to = setTimeout(() => {
console.log("Done after 2000 ms");
callback();
}, 2000);
sequenceState.onStop = () => {
clearTimeout(to);
callback(true);
};
}
function task2(callback) {
console.log("Task 2...");
let to = setTimeout(() => {
console.log("Done after 3000 ms");
callback();
}, 3000);
sequenceState.onStop = () => {
clearTimeout(to);
callback(true);
};
}
async.waterfall(
[
task1,
task2
],
function (err, results) {
console.log("End of tasks");
});
setTimeout(() => {
console.log("Stopped after 2500 ms !");
stop();
}, 2500);
<script src="https://cdn.jsdelivr.net/npm/async#2.6.0/dist/async.min.js"></script>
Does anyone have a better way of implementing this ?

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 ?

How can I redirect a stream based in an "internal state"

I'm writing a plugin for gulp that uses a web service and depending on the response do one or other thing. The algorithm is something like this:
stream1 = through.obj(function(src, enc, cb) {
if src.is_a_buffer()
http_request(options)
http_request.on('response', function () {
if (statusCode = 200) {
/* Normal course, everything works fine here */
do_something()
return cb()
} else {
/* Exception course, although the stream2 is created, is never executed */
stream1.pipe(stream2())
}
}, function (cb) {
cb()
});
stream2 = through.obj(function(src,enc,cb) {
do_other_stuff()
stream2.push(src)
return cb()
}, function (cb) {
cb()
});
When I run the code stream2 never excecutes.
Since i'm new to node streams, I think i misunderstood something. Can any of you guys help me out understanding what am I getting wrong here?
When you call stream1.pipe(stream2()), stream1 has already emitted data (possibly all of it); making that call won't pass the execution on to stream2. There are a couple of ways this can be handled depending on your needs:
NOTE: I'm just modifying the original pseudocode here
Option 1:
Don't bother with a stream2 and just call do_other_stuff() directly:
stream1 = through.obj(function(src, enc, cb) {
if src.is_a_buffer()
http_request(options)
http_request.on('response', function () {
if (statusCode = 200) {
/* Normal course, everything works fine here */
do_something()
cb()
} else {
do_other_stuff()
cb()
}
}, function (cb) {
cb()
});
Option 2:
If you need stream2 for other purposes, pull the through.obj() callback out in to its own callable function and call it directly from your else clause.
stream1 = through.obj(function(src, enc, cb) {
if src.is_a_buffer()
http_request(options)
http_request.on('response', function () {
if (statusCode = 200) {
/* Normal course, everything works fine here */
do_something()
return cb()
} else {
processStream2(src, enc, cb)
}
}, function (cb) {
cb()
});
function processStream2(src, enc, cb) {
do_other_stuff()
return cb()
}
stream2 = through.obj(processStream2, function (cb) {
cb()
});
I hope that helps :)

Multiple Async series causing wrong behavior in 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.

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