Create a gist now

Instantly share code, notes, and snippets.

Fan-in promise resolution
// Return a promise that will resolve when and only
// when N of the supplied promises resolve. The
// resolution value will be an array containing the
// resolution values of the triggering promises.
// TODO: Figure out the best strategy for rejecting.
function whenN(n, promises) {
var toResolve, values, promise;
toResolve = Math.max(0, Math.min(n, promises.length));
values = [];
promise = new Promise();
// Resolver for promises. Captures the value and resolves
// the returned promise when toResolve reaches zero.
// Overwrites resolver var with a noop once promise has
// be resolved to cover case where n < promises.length
function resolver(val) {
values.push(val);
if(--toResolve === 0) {
resolver = noop;
promise.resolve(values);
}
}
// Wrapper so that resolver can be replaced
function resolve(val) {
resolver(val);
}
// Rejecter for promises. Rejects returned promise
// immediately, and overwrites rejecter var with a noop
// once promise to cover case where n < promises.length.
// TODO: Consider rejecting only when N (or promises.length - N?)
// promises have been rejected instead of only one?
function rejecter(err) {
rejecter = noop;
promise.reject(err);
}
// Wrapper so that rejecer can be replaced
function reject(err) {
rejecter(err);
}
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
return promise;
}
function whenAll(promises) {
return whenN(promises.length, promises);
}
function whenAny(promises) {
return whenN(1, promises);
}
@unscriptable

Nice! I've been meaning to write this for a while now! Need to think about the reject use cases as you noted.

@unscriptable

Just glancing at this code... If I am not mistaken, for any case where n < promises.length or if any promise calls reject(), then you'll get errors because the promise could get completed (resolved/rejected) more than once.

@briancavalier
Owner

Yeah, you're absolutely right, I need to guard against that. I've been thinking about how to use function replacement/rewriting once the resolve/reject has fired.

@briancavalier
Owner

Here's a quick attempt at a version that uses function replacement to avoid the reject problem. I also applied the same pattern to resolving. Def more verbose, but seems decent. I tested resolving, but haven't tested rejecting yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment