yaoxing
yaoxing

Reputation: 4183

nodejs: wait for other methods to finish before executing

say I have 2 methods:

function A(callback) { ... }
function B(callback) { ... }

I want to execute:
function C();
after both A and B are finished.
what we usually do is to put function C in the callback like:

A(function() {
  B(function() {
    C();
  });
});

now if both A and B takes a long time, I don't want B to execute after A has been finished. instead I want to start them at the same time to enhance performance.
what I'm thinking is to implement something like a semaphore (not really a semaphore of course), it fires an event after both A and B are finished. so that I can call C from within the event.

what I want to know is, is there any library implemented the above function already? I believe I'm not the first one who wants to do it.
any help is appreciated.

Upvotes: 5

Views: 17334

Answers (5)

Salahin Rocky
Salahin Rocky

Reputation: 435

we can aync and await for this purpose for example:

async function Foo(){
   // function logic
}

and this Foo function as:

await Foo();

Upvotes: 0

Richard Nienaber
Richard Nienaber

Reputation: 10564

If you're running on ES6, you can use Promise.all. Here is the example code rewritten:

Promise.all([
  new Promise((resolve) => A(() => resolve())),
  new Promise((resolve) => B(() => resolve())),
]).then(() => {
  C()
}).catch(err => {
  // handle errors from function A, B and C
})

Upvotes: 0

Rob Raisch
Rob Raisch

Reputation: 17357

For the sake of completeness and as mentioned above, the same result can be achieved using an external object to hold completion state where both A() and B() check to see if the other has completed and if so, invokes C(). As in:

var results={};

function onComplete(){
    if(results['A'] && results['B'] && !results['C']) {
        C();
    }
}

function A(){
    // ... 
    results['A']=true;
    onComplete();
}

function B(){
    // ... 
    results['B']=true;
    onComplete();
}

The results object can be replaced by adding a 'isComplete' flag to both A() and B(), as in:

function A(){
    // ...
    A.isComplete=true;
    onComplete();
}

And modifying onComplete to check this new flag:

function onComplete(){
    if(A.isComplete && ...
}

Or, the same using events:

var util=require('util'),
    events=require('events'),
    EventEmitter=events.EventEmitter;

function F(){
    EventEmitter.call(this); // init ancestor
}

util.inherits(F,EventEmitter); // assign ancestor

F.prototype.A=function(){
    var self=this;
    console.log('running A()');
    setTimeout(function(){ // simulate long running code - 5 seconds
        F.prototype.A.isComplete=true;
        self.emit('complete','A');
    },5000);
};

F.prototype.B=function(){
    var self=this;
    console.log('running B()');
    setTimeout(function(){ // simulate long running code - 3 seconds
        F.prototype.B.isComplete=true;
        self.emit('complete','B');
    },3000);
};

F.prototype.C=function(){
    console.log('running C()');
};

var f=new F;
f.on('complete',function(which){ // onComplete handler
    console.log(which+'() is complete');

    if(F.prototype.A.isComplete && F.prototype.B.isComplete){
        f.C();
    }
});

// start it up
f.A();
f.B();

which, when run, will produce:

>node example.js
running A()
running B()
B() is complete
A() is complete
running C()
>

Upvotes: 2

Muhammad Umer
Muhammad Umer

Reputation: 2308

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

from: https://github.com/caolan/async

so in your case:

async.parallel([funcA,funcB],funcC);

//function definitions
function funcA() {...}
function funcB() {...}
function funcC() {...}

without modules i guess it would look something like this:

var numFuncs = 2;
A(D);
B(D);

and then 
function D() {
if(numFuncs==0){ C(); } else {
numFuncs--; 
}}

or like this:

A(D(C));
B(D(C));


function D() {
    var x = process.funcsToCall= process.funcsToCall || {};
    var f=arguments[0];
    (!x.hasOwnProperty(f.name))?x[f.name]=0:x[f.name]++;
    return function(){
       (x[f.name]==0)?f():x[f.name]--;
    }
}

Upvotes: 1

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123433

To expand on my comment...

async is a commonly used asynchronous flow control library for Node.js.

Its async.parallel() would probably do well for this:

async.parallel([
    function(done) {
        A(function () {
            done(null);
        });
    },

    function(done) {
        B(function () {
            done(null);
        });
    }
], function (err) {
    C();
});

It's possible that this can be shortened, but it depends on how each function interact with callbacks and whether they follow the common Node.js pattern of error-first callbacks:

async.parallel([A, B], C);

Upvotes: 5

Related Questions