Suppose I use Node.js to try to run two async calls to get some answers. I know there's an async package, where you can just pass two functions, and an optional callback.
async.parallel([fun1(){callback(null,1);},
fun2(){callback(null,2);}],
function(err, results) {
});
But suppose I have a priority now, if fun1 returns a value, then I do not need fun2's answer, only if fun1 returns null, then I wait for fun2. So I don't want to use the callback function, because the callback waits for both functions to finish, and fun2 may take very long.
Right now I just use a very exhaustive way by creating a callback function for both async calls.
function(){
var theAnswer,FromFun1,FromFun2;
var reply1,reply2;
fun1(reply1="answered";FromFun1=1;complete());
fun2(reply2="answered";FromFun2=2;complete());
function complete(answer){
if(reply1=="answered"){
theAnswer=FromFun1;
}else if(reply1==null){
// Don't do anything because fun1 is not finished running.
}else if(reply2=="answered"){
theAnswer=FromFun2;
}else{
// Both have no answer, err.
}
}
}
Is there a better way to do this?
The trick I've used for this scenario is to return "done" in first argument of the callback:
async.parallel([
function(callback){
callback("done",1);
},
function(callback){
callback(null,2);
}
], function(err, results) {
console.log(err, results); // done [ 1 ] or done [ 1 , 2 ]
});
Sounds like a hack and I don't usually do it but in some rare cases like this one, it actually keeps the code clean... just document it so that others know what you intention is.
Its better to use waterfall instead of parallel execution coz you can pass the result form your previous function to next one as argument.
async.waterfall([
function(callback){
callback(null, 'one');
},
function(arg1, callback){
// arg1 now equals 'one'
if(arg1==null){
// it will not take time it returns immediately
callback(null, 'done');
}else{
// do your another stuff here fun2 logic here
// it will not be executed and so it dont take time
}
}
], function (err, result) {
// result now equals 'done'
});
Related
I have a function:
var inRendering = false;
function render() {
if (inRendering) {
requestAnimationFrame(render);
} else {
inRendering = true;
requestAnimationFrame(function () {
longAction();
inRendering = false;
});
}
}
I have to unit-test it. To test a situation of concurrent call of render.
Help me, please? Is such concurrent call ever possible in JavaScript? Thanks.
P.S. I wrote a test which obviously doesn't work (see comment): https://gist.github.com/kuraga/b0aa3d66fc0620f03b11
you can use async module, specifically async.parallel in order to run two functions in parallel
for example:
async.parallel([
function(callback){
// run your function once
},
function(callback){
// run your function the second time
}
],
// optional callback
function(err, results){
// the results array will equal ['one','two'] even though
// the second function had a shorter timeout.
});
So I'm using async, this function will output some ids and the result of the count but not "callback"
getWaitingGames: function(req,res){
var me = req.session.passport.user;
var rez=[];
console.log("meeeeeeeeee");
console.log(me);
GivenQuestion.find().where({user_id:me}).exec(function(err,data){
var tpm_id = data[0].partie_id;
console.log(tpm_id);
console.log(data);
async.each(data, function(d, callback) {
var parties = [];
if (d.partie_id != tpm_id){
console.log(d.partie_id);
parties.push(d.partie_id);
tpm_id=d.partie_id;
GivenQuestion.count().where({
partie_id: d.partie_id,
user_id: me
}).exec(function(e, countRes) {
console.log(countRes);
if (countRes==2){
rez.push(d.partie_id);
callback();
}
});
}
}, function(err) {
if (err) {
console.log(error);
}
else{
console.log("callback");
res.ok(rez);
}
});
});
},
Why is that? I cannot find want I'm doing wrong, seems to be the same as in the doc and I've used it before in the same way
Edit: yes I can put the callback after the if like you guys suggested but then I get an empty rez. Why?
Does async only works one level? (I mean ,here I'm inside 2 .exec())
There is a small problem in the code. In case of async.each method, in the second parameter which is a processing function, we have to call callback() to move to the next element. If we don't call callback() then if will never come out of function.
In your case, you are making call to callback() only if if (countRes==2), but how about other scenarios? In other scenarios async.each() will wait for callback() to process next item in the data, but since we are not calling callback() so goes waiting for it.
Solution : Make call to callback() outside of if (countRes==2). Please check below code snippet for more details:
GivenQuestion.count().where({
partie_id: d.partie_id,
user_id: me
}).exec(function(e, countRes) {
console.log(countRes);
if (countRes==2){
rez.push(d.partie_id);
}
callback();
});
Please revert in case you still find the issue.
Need two inputs to have more insight about the issue:
Did you check that the condition if (countRes==2) results to true? Because if this condition evaluates to false, rez will always be empty.
If if (countRes==2) evaluates to true, then what is the value of d.partie_id in that case?
Please provide these inputs.
Also, async.each does work at multiple levels. I did an implementation where I used async inside async. So, it doesn't look like the fault of async.
I am a node.js noob trying to use async.waterfall.
I have problems to get from the last task of the waterfall array to the final callback method.
In the example below I am passing the callback to doSomethingAsync, but when I want to execute the callback inside doSomethingAsync I get TypeError: object is not a function. I don't understand. Thank you for your thoughts
EDIT:
The first task of the waterfall is the creation of a Mongo document. The callback of the save() function is function(err){...}.
var session = createSession(); // session is a Mongoose model
async.waterfall([
function (callback) {
...
session.save(callback); // Model.save(function(err){...}
},
function (callback) {
doSomethingAsync(session, callback)
}
], function (err, session) {
});
function doSomethingAsync(session, callback){
doSomething(function(err){
callback(err,session);
}
}
callback(err,session);
^
TypeError: object is not a function
My guess is that the problem lies in code you have removed. More specifically, you probably had a function in the waterfall right before the one you've shown that calls doSomethingAsync().
The way async.waterfall() works is that it passes on any arguments passed to the callback to the next function in the function list. So the previous function is probably doing something like callback(null, { foo: 'bar' }) and your callback argument in the next function is actually { foo: 'bar' } and the second argument is the real callback. It really depends on how many arguments you passed previously to the callback.
So assuming you just pass 1 item, you would change the function definition from:
function (callback) {
doSomethingAsync(session, callback)
}
to:
function (someResult, callback) {
doSomethingAsync(session, callback)
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I am in the process of relearning Javascript and last week when writing this code for a university assignment I think that there is probably a much better way of executing this code
app.get('/member/all', function(req, res) {
connection.query('CALL GetAllMembers()', function(err,rows){
connection.query('CALL CountMembers()', function(err, allMembers){
console.log(err);
connection.query('CALL CountAllIndMembers()', function(err,indMembers){
console.log(err);
connection.query('CALL CountInactiveMembers()', function(err,inactiveMembers){
console.log(err);
connection.query('CALL CountAllMembersInGroups()', function(err,groupMembers){
console.log(err);
res.render('members', {members : rows[0], title : "All Members", groupMembers : groupMembers[0][0].AllGrpMembers,
inactiveMembers : inactiveMembers[0][0].AllInactiveMembers, indMembers : indMembers[0][0].AllIndMembers,
allMembers : allMembers[0][0].AllMembers, statistics : true});
});
});
});
});
});
});
});
When I was trying to declare variables under the app.get such as var allMembers... when the callback was executed I was unable to set allMembers = rowsFromTheCallback. It seemed that it was a local variable to that callback. I'm sure this is something to do with the variable scope and/or hoisting. Just wanted to ask you guys if there would be a better way to do this as even though this function works. It is very ugly to look at haha!
Thanks in advance
Jack
As far as scope goes, all the inner functions should be able to read and write to the outer variable unless it is shadowed by an inner variable declaration or function parameter.
The problem you are having might be related to the async-ness of the code. See this code:
function delay(n, cb){
setTimeout(function(){ bs(delay) }, delay);
}
function main(){
var allMembers = 17;
delay(500, function(){
console.log(allMembers); // This looks at the outer "allMembers"
allMembers = 18;
delay(200, function(allMembers){ // <-- SHADOW
console.log(allMembers); // This looks at the allMembers from "delay 200"'s callback
allMembers = 42;
});
delay(300, function(){
console.log(allMembers); //This is the outside "allMembers" again
});
});
return allMembers; // Still 17!
}
main();
main will return before the setTimeouts have even fired so its going to return the original value of that variable. In order to wait for the inner callbacks to run, the only way is to make main take a callback to signa when its done, instead of just returning.
function main(onResult){
delay(500, function(){
//...
onResult(allMembers);
});
// <-- no return value
});
main(function(allM){
console.log(allM);
});
See async library: https://github.com/caolan/async
async.series([
getAllMembers,
countMembers,
...
], function(err, results) {
// err contains an error if any of the functions fails. No more functions will be run.
// results is an array containing results of each function if all the functions executed without errors
}));
function getAllMembers(callback) {
connection.query('CALL CountMembers()', callback);
}
function countMembers(callback) {
...
}
If the execution order of the functions does not matter, async.parallel can be used instead of async.series.
There is power in using a library to handle and encapsulate "Continuation Passing Style" (CPS) interactions with your asynchronous calls. The following code isn't from a library, but I'm going to walk through it and use it as an example of one way to implement CPS.
Setting up a scope appropriate queue is the first step. This example uses about the most simple method for doing so:
var nextList = [];
After that we need a method to handle our first case, the need to queue tasks to be performed in the future. In this case I was focused on performing them in order so I named it next.
function next() {
var todo,
current,
task,
args = {};
if (arguments.length > 0) { // if called with parameters process them
// if parameters aren't in an array wrap them
if (!Array.isArray(arguments['0'])) {
todo = [arguments];
} else { // we were passed an array
todo = [];
arguments['0'].forEach(function (item) {
// for each item we were passed add it to todo
todo.push(item);
});
}
nextList = todo.concat(nextList);
// append the new items to the end of our list
}
if (nextList.length > 0) { // if there are still things to do
current = Array.prototype.slice.apply(nextList.shift());
task = current[0];
args = current.slice(1);
task.apply(null, args); // execute the next item in the list
}
}
This allows us to make calls like:
.map(function (filepath) {
tasks.push(
[
handleAsset,
{
'path': filepath,
}
]
);
});
tasks.push([done]);
next(tasks);
This will call handleAsset, which is async, once for each file, in order. This will allows you to take your code and change each of the nested calls into a separate function in the form:
function memberAll() {
app.get('/member/all', function(req, res) {
if (err) {
handleError(err, 'memberAll');
} else {
next(getAllMembers, 'parameters to that call if needed');
}
});
}
where handleError is a common error handler, and the next call allows you to pass on relevant parameters to the next function that is needed. Importantly in the success side of the if statement you could either:
conditionally call one of several functions
call next with an array of calls to make, for instance if you had functions for processFolder and processFile you could expect that processing a folder might involve processing other folders and files and that the number would vary
do nothing except call next() with no parameters and end the current branch
Embellishments can include writing a clean function for emptying the nextList, adding items to nextList without calling an item from the list, etc. The alternative at this point is to either use an existing library for this or to continue writing your own.
Let's say I have multiple functions func1, func2, func3, etc.....
And they all contain an AJAX/async function within them:
function funcX(){
// some ajax request
}
If in a main function I am calling func1, func2, func3 sequentially like so:
$(document).ready(function(){
func1();
func2();
func3();
...
}
Will each ajax/async function's call be certain to execute in the order of their parent functions? At first I thought they might be, but the behavior of my program seems to be suggesting otherwise...
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
Will each ajax/async function's call be certain to execute in the order of their parent functions?
They should execute in order, but their internal callbacks can be called in any order.
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
You could use a promise, and execute the next function when the promise has been resolved.
This example uses jQuery...
var fn1 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 1 done.") && d.resolve();
}, Math.random() * 1300 + 800);
return d.promise();
};
var fn2 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 2 done.") && d.resolve();
}, 500);
return d.promise();
};
$.when(fn1(), fn2()).then(function () {
setTimeout(function () {
$("body").text("All done.");
}, 300);
});
jsFiddle.
We use $.when() and pass the invoked functions we want to execute to it. We then use then() to show a final message (I placed a setTimeout() here so you can see the last resolved function's message in the document).
Each of these functions have their own deferred object which return the promise. A setTimeout() mocks an XHR for example's sake. When this callback is executed, we resolve the deferred object.
Once both have been deferred, we reach the callback for then().
To serialize tasks, I've written a helper function, which can also be found in my earlier answer:
function serializeTasks(arr, fn, done)
{
var current = 0;
fn(function iterate() {
if (++current < arr.length) {
fn(iterate, arr[current]);
} else {
done();
}
}, arr[current]);
}
It takes an array of values (in your case those are actually functions), a loop function and a completion handler. Below is the loop function:
function loopFn(nextTask, fn)
{
fn(nextTask);
}
It accepts an intermediate completion function as the first argument and each element of the aforementioned array.
To set everything in motion:
serializeTasks([func1, func2, func3], loopFn, function() {
console.log('all done');
});
Your functions are called with a single argument which should be passed to the AJAX success callback, e.g.
func1(nextTask)
{
$.ajax({
...,
success: nextTask
});
}
The order in which the asynch results are returned is not deterministic, and may wary every time.
func2 might complete before func1 etc
It is important to ensure correct order of execution. One pattern is to call the next function in the success callback of the prior function
Ex:
$.get("/someUrl",function(){
$.get("/nextAjaxCall", function(){
.....
});
});
If the dependency chain is very simple, I don't think it's necessary to introduce a framework to handle this
Or look at async library and it's awesomeness !
async