public
Last active

Fan-in promise resolution

  • Download Gist
whenN.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 
// 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);
}

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

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.

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.

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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.