Asynchronous grouped callback

9th January 2014

When programming Javascript it is sometimes neccessary to start multiple methods, let them run all at the same time and then have a single callback that is called when all of the methods are finished. Dealing with raw Javascript, it is convenient to use a simple object to do the magic for you and just supply methods to add, that is what the class below does:

function AsyncBin(callback) {
if (callback===undefined) callback = function() {};

this._async = {};
this._id = 0;

this._callback = callback;
this._done = false;
}

AsyncBin.prototype.isWaiting = function() {
for (var key in this._async) {
if (this._async.hasOwnProperty(key) && this._async[key]) {
return true;
}
}

return false;
};

AsyncBin.prototype.reset = function() {
this._done = false;

//mark all of the callback methods as not been completed
for (var key in this._async) {
if (this._async.hasOwnProperty(key)) {
this._async[key] = true;
}
}
};

AsyncBin.prototype.ready = function(callback) {
if (callback!==undefined) {
this._callback = callback;
}

//run the test once if it has not already been called
this._done = true;

if (!this.isWaiting()) {
this._callback();
}
};

AsyncBin.prototype.callback = function(callbackId) {
if (callbackId===undefined) {
callbackId = this.add();
}

var self = this;

return function() {
if (self._async.hasOwnProperty(callbackId) && self._async[callbackId]) {
self._async[callbackId] = false;

if (self._done && !self.isWaiting()) {
self._callback();
}
}
};
};

AsyncBin.prototype.remove = function(callbackId) {
delete this._async[callbackId];
};

AsyncBin.prototype.add = function() {
var callbackId = String(this._id++);

this._async[callbackId] = true;

return callbackId;
};

Usage

You can use the class below as follows:

var startTime = new Date().getTime();
var bin = new AsyncBin(done);
longTask(bin.callback());
longTask2(bin.callback());
bin.ready();

function done() { alert("Took " + (new Date().getTime() - startTime) + " ms"); };
function longTask(callback) { setTimeout(callback, 1000); };
function longTask2(callback) { setTimeout(callback, 2000); };

You can also create callback ids in advance and manually remove them from the bin if you wish:

var bin = new AsyncBin(function() {
alert("done");
});

var done1 = bin.add();

longTask(bin.callback(done1));

bin.ready();

setTimeout(function() {
done1.cancel(); //remove the callback method from the bin
bin.ready(); //force the bin to execute its done method if the other items are also ready
}, 1000);

Make a comment

Contribute to this article and have your say.