Skip to content

Instantly share code, notes, and snippets.

@fictorial
Created December 1, 2009 15:54
Show Gist options
  • Save fictorial/246384 to your computer and use it in GitHub Desktop.
Save fictorial/246384 to your computer and use it in GitHub Desktop.
/**
* A PromiseGroup accepts a number of named promises, waits for each to
* complete (succeed, fail, timeout, cancel), then emits an event to signify
* that all accepted promises have completed.
*
* Names of promises are arbitrary but must be strings and must be unique
* across the set of all named promises a PromiseGroup accepts.
*
* When an individual promise completes, a PromiseGroup emits "one_done"
* passing the name of the promise, the outcome ('ok', 'fail', 'cancel', or
* 'timeout') and the promise's result. For 'ok' the result is an array of
* arguments from the promise's emitSuccess. For 'error' its just the error.
*
* When all promises have completed, the PromiseGroup emits an event "all_done"
* passing an object that describes the outcome of each named promise. The
* object takes the form:
*
* { ok: { 'name': result, ... },
* fail: { 'name': error, ... },
* timeout: { 'name': null, ... },
* cancel: { 'name': result, ... } }
*
* where 'name' is the name of a promise, 'result' is an array,
* 'error' is a string.
*
* There is no implied dependency between promises.
*
* There's no (public) way to know if a Promise has fired so be sure
* to not have a PromiseGroup accept Promises that have already fired
* as you will wait for the PromiseGroup forever.
*/
function PromiseGroup() {
this.promises = {ok:{}, fail:{}, cancel:{}, timeout:{}, waiting:{}};
}
exports.PromiseGroup = PromiseGroup;
require('sys').inherits(PromiseGroup, process.EventEmitter);
PromiseGroup.prototype.add = function (promise, name) {
if (!promise || !(promise instanceof process.Promise))
throw new Error("Promise required");
if (!name || typeof(name) != "string" || name.length === 0)
throw new Error("Promise name required");
var self = this;
self.promises.waiting[name] = 1;
function one_done(name, outcome, result) {
self.promises[outcome][name] = result;
delete self.promises.waiting[name];
self.emit("one_done", name, outcome, result);
var has_more_coming = false;
for (var x in self.promises.waiting) {
has_more_coming = true;
break;
}
if (!has_more_coming) {
delete self.promises.waiting;
self.emit("all_done", self.promises);
}
};
promise.addCallback(function () {
one_done(name, 'ok', Array.prototype.slice.call(arguments));
}).addErrback(function (e) {
var outcome = 'fail', result = e;
if (e instanceof Error && e.message === "timeout") {
outcome = 'timeout';
result = null;
}
one_done(name, outcome, result);
}).addCancelback(function () {
one_done(name, 'cancel', Array.prototype.slice.call(arguments));
})
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment