I have a question about the best way to chain callbacks together while passing data between them. I have an example below which works, but has the flaw that the functions have to be aware of the chain and know their position / whether to pass on a call back.
function first(data, cb1, cb2, cb3){
console.log("first()", data);
cb1(data,cb2, cb3);
}
function second(data, cb1, cb2) {
console.log("second()",data);
cb1(data, cb2);
}
function third(data, cb) {
console.log("third()",data);
cb(data);
}
function last(data) {
console.log("last() ", data);
}
first("THEDATA", second, third, last); // This work OK
first("THEDATA2", second, last); // This works OK
first("THEDATA3", second); // This doesn't work as second tries to pass on a callback that doesn't exit.
I can think of a number of ways around this, but they all involve in making the called functions aware of what is going on. But my problem is that the real functions I’m dealing with already exist, and I don’t want to modify them if I can avoid it. So I wondered if I was missing a trick in terms of how these could be called?
Thanks for the answers, and I agree that promises are probably the most appropriate solution, as they meet my requirements and provide a number of other advantages.
However I have also figured out how to do what I want without involving any extra modules.
To recap the specific ask was to:
chain together a number of functions via callbacks (this is so the first function can use a non-blocking I/O call the other functions are dependent upon),
while passing arguments (data) between them, and
without the existing functions having to be modified to be aware of their position in the callback chain.
The 'trick' I was missing was to introduce some extra anonymous callback functions to act as links between the existing functions.
// The series of functions now follow the same pattern, which is how they were before
// p1 data object and p2 is a callback, except for last().
function first(data, cb){
console.log("first()", data);
cb(data);
}
function second(data, cb) {
console.log("second()",data);
cb(data);
}
function third(data, cb) {
console.log("third()",data);
cb(data);
}
function last(data) {
console.log("last() ", data);
}
// And the named functions can be called pretty much in any order without
// the called functions knowing or caring.
// first() + last()
first("THEDATA", function (data) { // The anonymous function is called-back, receives the data,
last(data); // and calls the next function.
});
// first() + second() + last()
first("THEDATA2", function (data) {
second(data, function (data){
last(data);
}); // end second();
}); // end first();
// first() + third()! + second()! + last()
first("THEDATA3", function (data) {
third(data, function (data){
second(data, function (data){
last(data);
}); // end third();
}); // end second();
}); // end first();
Promises are the way to go as they provide following benefits :
Sequential callback chaining
Parallel callback chaining (kind of)
Exception handling
Easily passing around the objects
In general, a Promise performs some operation and then changes its own state to either rejected - that it failed, or resolved that its completed and we have the result.
Now, each promise has method then(function(result), function(error)). This then() method is executed when the Promise is either resolved or rejected. If resolved, the first argument of the then() is executed, with the result and if Promise gets rejected 2nd argument is executed.
A typical callback chaining looks like :
example 1 :
someMethodThatReturnsPromise()
.then(function (result) {
// executed after the someMethodThatReturnsPromise() resolves
return somePromise;
}).then(function (result) {
// executed after somePromise resolved
}).then(null, function (e) {
// executed if any of the promise is rejected in the above chain
});
How do you create a Promise at the first place ? You do it like this :
new Promise (function (resolve, reject) {
// do some operation and when it completes
resolve(result /*result you want to pass to "then" method*/)
// if something wrong happens call "reject(error /*error object*/)"
});
For more details : head onto here
Best practice would be to check whether the callback you are calling is provided in the args.
function first(data, cb1, cb2, cb3){
console.log("first()", data); // return something
if(cb1){
cb1(data,cb2, cb3);
}
}
function second(data, cb1, cb2) {
console.log("second()",data); // return something
if(cb1){
cb1(data, cb2);
}
}
function third(data, cb) {
console.log("third()",data); // return something
if(cb){
cb(data);
}
}
function last(data) {
console.log("last() ", data); // return something
}
first("THEDATA", second, third, last); // This work OK
first("THEDATA2", second, last); // This works OK
first("THEDATA3", second);
This will work fine.
Alternatively there are many more options like Promises and Async library e.tc
Maybe you want to try it this way, yes right using "Promise" has more features, but if you want something simpler, we can make it like this, yes this is without error checking, but of course we can use try / catch
function Bersambung(cb){
this.mainfun = cb
this.Lanjut = function(cb){
var thisss = this;
if(cb){
return new Bersambung(function(lanjut){
thisss.mainfun(function(){
cb(lanjut);
})
})
} else {
this.mainfun(function(){});
}
}
}
//You can use it like this :
var data1 = "", data2 = "" , data3 = ""
new Bersambung(function(next){
console.log("sebelum async")
setTimeout(function(){
data1 = "MENYIMPAN DATA : save data"
next();
},1000);
}).Lanjut(function(next){
if(data1 == ""){
return; // break the chain
}
console.log("sebelum async 2")
setTimeout(function(){
console.log("after async")
console.log(data1);
next();
},1000)
}).Lanjut();
Okay this might not be ideal but it works.
function foo(cb = [], args = {}) // input a chain of callback functions and argumets
{
args.sometext += "f";
console.log(args.sometext)
if (cb.length == 0)
return; //stop if no more callback functions in chain
else
cb[0](cb.slice(1), args); //run next callback and remove from chain
}
function boo(cb = [], args = {})
{
newArgs = {}; // if you want sperate arguments
newArgs.sometext = args.sometext.substring(1);
newArgs.sometext += "b";
console.log(newArgs.sometext)
if (cb.length == 0)
return;
else
cb[0](cb.slice(1), newArgs);
}
//execute a chain of callback functions
foo ([foo, foo, boo, boo], {sometext:""})
Related
I have a function that processes an array of data (first parameter) and, once the procesing is finished, it invokes only one time a callback function (second parameter). I'm using forEach to process data item by item, consisting the processing of each item in some checkings and storing the param in database. The function storeInDB() does the storing work and uses a callback (second parameter) when the item has been stored.
A first approach to the code is the following:
function doWork(data, callback) {
data.forEach(function (item) {
// Do some check on item
...
storeInDB(item, function(err) {
// check error etc.
...
callback();
});
});
}
However, it's wrong, as the the callback function will be invoked several times (as many as element in the data array).
I'd like to know how to refactor my code in order to achieve the desired behaviour, i.e. only one invocation to callback once the storing work is finished. I guess that async could help in this task, but I haven't find the right pattern yet to combine async + forEach.
Any help is appreciated!
You can use a library such as async to do this, although I would recommend using promises if possible. For your immediate problem you can use a counter to determine how many storage calls have completed and call the callback when the total number are completed.
let counter = 0;
data.forEach(function (item) {
// Do some check on item
...
storeInDB(item, function(err) {
// check error etc.
counter++
if (counter == data.length) {
callback();
}
});
});
you can also utilize the three parameters passed to the function to execute on each array method
function doWork(data, callback) {
data.forEach(function (value,idx,arr) {
// Do some check on item
...
storeInDB(arr[idx], function(err) {
// check error etc.
...
if ( (idx + 1) === arr.length ) {
callback();
}
});
});
}
If storeInDB function returns a promise, you can push all async functions to an array and use Promise.all. After all tasks run successfully, It will invokes callback function.
Hope this helps you.
function doWork(data, callback) {
let arr = [];
data.map(function(itm) {
// Do some check on item
...
arr.push(storeInDB(item));
});
Promise.all(arr)
.then(function(res) {
callback();
});
}
I'm trying to call an asynchronous function (the 'child') from within another asynchronous function (the 'parent'). To make things complicated, the parent function exposes a dual API (callback + promise). This is done using the Q library's defer() and nodeify(), like so:
var parent = function(p_arg, callback) {
var deferred = Q.defer();
child(arg, function(err, cb_arg) {
if(err)
deferred.reject(err);
else
deferred.resolve(cb_arg);
});
//If a callback was supplied, register it using nodeify(). Otherwise, return a promise.
if(callback)
deferred.promise.nodeify(callback);
else
return deferred.promise;
}
Note that the child function doesn't return a promise. Now depending on the value of the argument passed to the child's callback (cb_arg), I might decide to make a fresh call to parent, albeit with a modified argument (p_arg). How do I do this keeping in mind the dual nature of the parent's API?
This is what I've been able to come up with so far:
child(arg, function(err, cb_arg) {
if(err)
deferred.reject(err);
else if(cb_arg.condition) {
/*Make a fresh call to parent, passing in a modified p_arg*/
if(callback)
parent(p_arg + 1, callback);
else
deferred.promise = parent(p_arg + 1);
/*
^ Is this the correct way to do this?
My goal is to return the promise returned by the fresh parent() call,
so how about I just assign it to the current parent() call's deferred.promise?
*/
}
else
deferred.resolve(cb_arg);
});
UPDATE: Okay, I just had a moment of clarity regarding this problem. I now think that what actually needs to be done is the following:
child(arg, function(err, cb_arg) {
if(err)
deferred.reject(err);
else if(cb_arg.condition) {
/*Make a fresh call to parent, passing in a modified p_arg*/
if(callback)
parent(p_arg + 1, callback);
else
parent(p_arg + 1)
.then(function(p_ret) {
deferred.resolve(p_ret);
}, function(p_err) {
deferred.reject(p_err);
});
}
else
deferred.resolve(cb_arg);
});
This pattern seems to work for my particular use case. But do let me know in case there are any glaring async-related errors with this approach.
How do I do this keeping in mind the dual nature of the parent's API?
Don't. Just always use promises when you have them available.
Btw, you can use Q.ncall instead of manually using a deferred. And you don't need to test explicitly for callbacks, nodeify will handle it if you pass undefined.
deferred.promise = parent(p_arg + 1);
Is this the correct way to do this?
My goal is to return the promise returned by the fresh parent() call,
so how about I just assign it to the current parent() call's deferred.promise?
You would use .then() and return a new promise from the callback to chain the actions, getting a new promise for the result of the last action.
var promise = child().then(function(res) {
if (condition(res))
return parent(); // a promise
else
return res; // just pass through the value
});
Update: […] This pattern seems to work for my particular use case.
That's a variation of the deferred antipattern. You don't want to use it. In particular, it seems to leave a dangling (never-resolved) deferred around with a callback attached to it, in the case that there is a callback passed.
What you should do instead is just
function parent(p_arg, callback) {
return Q.nfcall(child, arg)
.then(function(cb_arg) {
if (cb_arg.condition) {
return parent(p_arg + 1, callback);
else
return cb_arg;
})
.nodeify(callback);
}
function squre(val) {
main.add(val,function(result){
console.log("squre = " + result); //returns 100 (2nd line of output)
return result;
});
}
console.log(squre(10)); // returns null (1st line of output)
I need 100 as output in both of the lines.
It rather depends on the nature of main.add(). But, by its use of a callback, it's most likely asynchronous. If that's the case, then return simply won't work well as "asynchronous" means that the code doesn't wait for result to be available.
You should give a read through "How to return the response from an AJAX call?." Though it uses Ajax as an example, it includes a very thorough explanation of asynchronous programming and available options of control flow.
You'll need to define squre() to accept a callback of its own and adjust the calling code to supply one:
function squre(val, callback) {
main.add(val, function (result) {
console.log("squre = " + result);
callback(result);
});
});
squre(10, function (result) {
console.log(result);
});
Though, on the chance that main.add() is actually synchronous, you'll want to move the return statement. They can only apply to the function they're directly within, which would be the anonymous function rather than spure().
function squre(val) {
var answer;
main.add(val, function (result) {
console.log("squre = " + result);
answer = result;
});
return answer;
}
You can't, as you have the asynchronous function main.add() that will be executed outside of the current tick of the event loop (see this article for more information). The value of the squre(10) function call is undefined, since this function doesn't return anything synchronously. See this snippet of code:
function squre(val) {
main.add(val,function(result){
console.log("squre = " + result);
return result;
});
return true; // Here is the value really returned by 'squre'
}
console.log(source(10)) // will output 'true'
and The Art of Node for more information on callbacks.
To get back data from an asynchronous function, you'll need to give it a callback:
function squre(val, callback) {
main.add(val, function(res) {
// do something
callback(null, res) // send back data to the original callback, reporting no error
}
source(10, function (res) { // define the callback here
console.log(res);
});
I found this code where it says that I can run some db queries asynchronously
var queries = [];
for (var i=0;i <1; i++) {
queries.push((function(j){
return function(callback) {
collection.find(
{value:"1"},
function(err_positive, result_positive) {
result_positive.count(function(err, count){
console.log("Total matches: " + count);
positives[j] = count;
callback();
});
}
);
}
})(i));
}
async.parallel(queries, function(){
// do the work with the results
}
I didn't get the part what is callback function how is it defined ? second in the queries.push, it is passing the function(j) what is j in this and what is this (i) for
queries.push((function(j){})(i));
I am totally confused how this code is working?
The loop is preparing an array of nearly-identical functions as tasks for async.parallel().
After the loop, given it only iterates once currently, queries would be similar to:
var queries = [
function (callback) {
collection.find(
// etc.
);
}
];
And, for each additional iteration, a new function (callback) { ... } would be added.
what is callback function how is it defined ?
callback is just a named argument for each of the functions. Its value will be defined by async.parallel() as another function which, when called, allows async to know when each of the tasks has completed.
second in the queries.push, it is passing the function(j) what is j in this and what is this (i) for
queries.push((function(j){})(i));
The (function(j){})(i) is an Immediately-Invoked Function Expression (IIFE) with j as a named argument, it's called immediately with i a passed argument, and returns a new function (callback) {} to be pushed onto queries:
queries.push(
(function (j) {
return function (callback) {
// etc.
};
})(i)
);
The purpose of the IIFE is to create a closure -- a lexical scope with local variables that is "stuck" to any functions also defined within the scope. This allows each function (callback) {} to have its own j with a single value of i (since i will continue to change value as the for loop continues).
Callback is one of the coolest features. Callback is just another normal function. You can actually pass a function itself as a parameter in another function.
Say function foo() does something. You may want to execute something else right after foo() is done executing. So, in order to achieve this, you define a function bar() and pass this function as a parameter to function foo()!
function foo(callback){
// do something
callback();
}
function bar(){
// do something else
}
foo(bar());
//here we can see that a function itself is being passed as a param to function foo.
For more understanding here's the right link.
And
queries.push((function(j){})(i));
in javascript, this is another way of calling a function.
function(){
// do something.
}();
You don't actually need to define a function name and it can be called directly without a name. You can even pass params to it.
function(a){
alert(a)'
}(10);
I have the following example code - the first part can result in an async call or not - either way it should continue. I cannot put the rest of the code within the async callback since it needs to run when condition is false. So how to do this?
if(condition) {
someAsyncRequest(function(error, result)) {
//do something then continue
}
}
//do this next whether condition is true or not
I assume that putting the code to come after in a function might be the way to go and call that function within the async call above or on an else call if condition is false - but is there an alternative way that doesn't require me breaking it up in functions?
A library I've used in Node quite often is Async (https://github.com/caolan/async). Last I checked this also has support for the browser so you should be able to npm / concat / minify this in your distribution. If you're using this on server-side only you should consider https://github.com/continuationlabs/insync, which is a slightly improved version of Async, with some of the browser support removed.
One of the common patterns I use when using conditional async calls is populate an array with the functions I want to use in order and pass that to async.waterfall.
I've included an example below.
var tasks = [];
if (conditionOne) {
tasks.push(functionOne);
}
if (conditionTwo) {
tasks.push(functionTwo);
}
if (conditionThree) {
tasks.push(functionThree);
}
async.waterfall(tasks, function (err, result) {
// do something with the result.
// if any functions in the task throws an error, this function is
// immediately called with err == <that error>
});
var functionOne = function(callback) {
// do something
// callback(null, some_result);
};
var functionTwo = function(previousResult, callback) {
// do something with previous result if needed
// callback(null, previousResult, some_result);
};
var functionThree = function(previousResult, callback) {
// do something with previous result if needed
// callback(null, some_result);
};
Of course you could use promises instead. In either case I like to avoid nesting callbacks by using async or promises.
Some of the things you can avoid by NOT using nested callbacks are variable collision, hoisting bugs, "marching" to the right > > > >, hard to read code, etc.
Just declare some other function to be run whenever you need it :
var otherFunc = function() {
//do this next whether condition is true or not
}
if(condition) {
someAsyncRequest(function(error, result)) {
//do something then continue
otherFunc();
}
} else {
otherFunc();
}
Just an another way to do it, this is how I abstracted the pattern. There might be some libraries (promises?) that handle the same thing.
function conditional(condition, conditional_fun, callback) {
if(condition)
return conditional_fun(callback);
return callback();
}
And then at the code you can write
conditional(something === undefined,
function(callback) {
fetch_that_something_async(function() {
callback();
});
},
function() {
/// ... This is where your code would continue
});
I would recommend using clojurescript which has an awesome core-async library which makes life super easy when dealing with async calls.
In your case, you would write something like this:
(go
(when condition
(<! (someAsyncRequest)))
(otherCodeToHappenWhetherConditionIsTrueOrNot))
Note the go macro which will cause the body to run asynchronously, and the <! function which will block until the async function will return. Due to the <! function being inside the when condition, it will only block if the condition is true.