14

I need to execute a bunch of asynchronous methods (client SQLite database), and call only one final callback.

Of course, the ugly way is:

execAll : function(callBack) {
        asynch1(function() {
            asynch2(function() {
                ...
                asynchN(function() {
                    callBack();
                })
            })
        });
    }

But I know there are better ways to do it. Intuitively I would detect when all call back has been called with a counter to call the final callback.

I think this is a common design-pattern, so if someone could point me in the right direction...

Thanks in advance !

Samuel
  • 5,439
  • 6
  • 31
  • 43

4 Answers4

21

this is easy

var callback = (function(){
    var finishedCalls = 0;
    return function(){
        if (++finishedCalls == 4){
             //execute your action here
        }
    };
})();

Just pass this callback to all your methods, and once it has been called 4 times it will execute.

If you want to use factory for this then you can do the following

function createCallback(limit, fn){
    var finishedCalls = 0;
    return function(){
        if (++finishedCalls == limit){
             fn();
        }
    };
}


var callback = createCallback(4, function(){
    alert("woot!");
});


async1(callback);
async2(callback);
async3(callback);
async4(callback);
Sean Kinsey
  • 37,689
  • 7
  • 52
  • 71
  • 1
    And if any such pattern exist, then this is it. – Sean Kinsey May 26 '10 at 10:05
  • Thanks a lot Sean, it was easy, and implement something like your first solution before seeing your answer. But I like more your callback factory, it is very elegant, i will use it ;-) – Samuel May 26 '10 at 10:48
  • And if it is a pattern, it has to have a name. Suggestions?! – ThomasH May 09 '11 at 14:48
  • Here is utilliy function same as this answer https://gist.github.com/4350633 read more here http://www.markandey.com/2012/12/how-to-get-output-of-multiple.html – Markandey Singh Dec 21 '12 at 06:57
  • If your application already use [underscorejs](http://underscorejs.org/), then you can call `_.after()`. Its working is exactly like factory given in this answer. – Junaid Jul 22 '15 at 10:10
8

I've written some async utilities you might find useful, allowing you to write your example as:

function(callback) {
    async.series([
        asynch1(),
        asynch2(),
        ...
        asynchN()
    ], callback);
}

Or, if you wanted to run them in parallel, as:

function(callback) {
    async.parallel([
        asynch1(),
        asynch2(),
        ...
        asynchN()
    ], callback);
}

There are loads of other useful functions like async map/reduce too:

http://caolanmcmahon.com/async.html

Hope that helps!

Caolan
  • 3,909
  • 1
  • 18
  • 8
2

You should consider using Deferred pattern for asynchronous methods. You can get more information from the this StackOverflow question and answers:

What are the differences between Deferred, Promise and Future in JavaScript?

The marked answer from jnewman was good actually!

Hope this helps.

Community
  • 1
  • 1
Anh Nguyen
  • 1,202
  • 14
  • 17
0

Promises can help manage this. There are two general scenarios - parallel and serial. Parallel can be accomplished using Promise.all(), serial is more complex - task B can only start when task A is done. Here's a bare-bones sample:

// returns a promise that resolves as the task is done
const wrap = (fn, delay) => new Promise(resolve => setTimeout(_ => resolve(fn()), delay));
const task = (fn, delay) => delay ? wrap(fn, delay) : Promise.resolve(fn());

// given a list of promises, execute them one by one.
const sequence = async l => l.reduce(async (a, b) => [].concat(await a, await b));

const tasks = [
    task(_ => console.log("hello world")),
    task(_ => console.log("hello future"), 1000)
];

sequence(tasks).then(_ => console.log("all done"));

You may need some ES6/7 translation to make this work in browsers or older node versions.