Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 917
yaoxing Avatar asked Aug 08 '13 01:08

yaoxing


2 Answers

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);
like image 198
Jonathan Lonowski Avatar answered Oct 02 '22 17:10

Jonathan Lonowski


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()
>
like image 35
Rob Raisch Avatar answered Oct 02 '22 15:10

Rob Raisch