User interacting with gold iPhone

Effective Thinking

Promise.any() – A Missing Use Case

My first exposure to promises in JavaScript was through the $q library in AngularJS 1.0. It is much more bare bones than modern promise implementations, but for me it was leaps and bounds better than the callback hell I’ve found myself stuck in. Since AngularJS 1.0, the promise specification has become more mature and native browser implementations have been popping up (though you’ll still need a polyfill). So, if you haven’t learned how to use promises, now might be the time!

PromiseOne of my “breakthrough” moments in learning about promises was when I read “You’re Missing the Point of Promises” by Domenic Denicola. A big concept I grasped after reading Domenic’s article is that promises make asynchronous return values relevant again. At the risk of being yet another basic explanation of promises, here is the gist of what that means for a developer.

When dealing with a standard asynchronous method, such as the $.get() method from jQuery (you shouldn’t trust jQuery “promises”), the return value is useless:

This completely discards a huge feature of the JavaScript language. With promises, the return value of asynchronous methods has actual relevance to the value returned by the asynchronous action:

There are plenty of other reasons why promises are better than callbacks. However, I want to talk about a recent problem I had to solve around aggregating multiple promises.

The Problem

I was trying to implement a search form on a site for one of our clients. When the user enters a search query I make several AJAX requests to different APIs. If any of these requests come back with successful results, I want to navigate to a page with the results. If one fails, that’s OK, as long as at least one succeeds. If they all fail I want to stay on the same page and display an error message.

Promise.all() implements a boolean AND operation on whether each promise rejects or resolves. For this problem, a boolean OR operation is needed. Since there is no existing implementation like this, I decided to write my own.

The Solution

I implemented a method I’m calling Promise.any(). It takes an array of promises and resolves if at least one of the promises resolves. The full implementation is at https://gist.github.com/jkjustjoshing/de488c63074370e28169#file-promise-any-js —  a slimmed-down version is shown below:

There are two main things happening here:

1. Each promise, whether it resolves or rejects, gets mapped to an internally used promise that resolves to an object with two values:

  • resolve – whether the initial promise was resolved or rejected
  • result – the resolve or reject value of the initial promise

2. Using Promise.all(), the internally used promises are inspected – if at least one is resolved, the returning promise is resolved. If none of the promises are resolved, the returning promise is rejected.

I can then solve my problem with the following code:

A few things to note about this solution:

1. If the Promise.any() promise resolves, any individual promises that rejected will appear to be null in the result array. This means that any promise that resolves to null will break this implementation. For my use case this isn’t an issue, but it is a large enough flaw that you won’t see my implementation included in any promise libraries.

2. If the Promise.any() promise rejects, it will reject with an array of all rejection objects. This is different from Promise.all(), which rejects to only the first promise that rejects.

Are there any better ways to do this? Let me know in the comments.

UPDATE – A friend of mine has pointed out that the Q promise library has a Q.allSettled() method, which is a similar implementation to my Promise.any() implementation. The Q implementation doesn’t suffer from the issues mine does and works in a more general situation. One more example why the author, Kris Kowal, is very well known for his work with promises!

  • Alex Mills
  • Cameron Martin

    Promise.race does this – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race. Although it would be better if it was named Promise.any.

    • Philippe Hébert

      Unfortunately it isn’t the case, Promise.race ‘ returns a promise that resolves or rejects **as soon as one of the promises in the iterable resolves or rejects**’.

  • @disqus_qWVhbC948g:disqus I don’t think that Promise.race does the same as Promise.any – the difference being if at least one promise failed in the race, the whole race fails (is rejected). The .any works if at least one succeeds, ignoring failed promises.

  • Alireza

    This is a great post. I just wanted to add a small missing feature you could have added to yours and it “Short Circuit”. Your .any method will wait for all promises to fulfill/fail before it updates the status of the returning promise to fulfilled / rejected. I have a scenario in which as soon as a Promise is resolved (the fastest async call that returns the expected response) I want my resulting promise to fulfill.
    Any ideas how to achieve it? It is not straight-forward to achieve it via Promise.race as well.

    • Alireza

      I actually found this awesome answer in StackOverflow which solves my problem:
      http://stackoverflow.com/a/21273983/1186837
      Effectively this is a chain of ‘invertPromise(Promise.all(invertPromise(promiseArray)))’ where invertPromise accepts a promise and returns a new one whose status is opposite (resolved => rejected, rejected => resolved).

  • Mina Luke

    I am using bluebird for this reason:
    http://bluebirdjs.com/docs/api/promise.any.html
    it supports both Some and Any

  • Philippe Hébert

    I have made my own implementation of this in a much more concise manner and without any null pointers or drawback –
    “`js
    Promise.any = function(promises) {
    return new Promise((resolve, reject) => {
    //!p && !q && !… = !(p || q || …)
    Promise.all(promises.map(p => {
    return new Promise((resolve, reject) => {
    p.then((res) => reject(res))
    .catch((err) => resolve(err));
    });
    }))
    //!(!(p || q || …)) = p || q || …
    .then((ps) => reject(ps))
    .catch((ps) => resolve(ps));
    });
    };
    “`
    Essentially all I am doing is doing boolean logic on the Promise.all (hence the p, q shenanigans in the comments).
    The only drawback is that unlike Promise.race, this will not succeed on the first successful promise – rather it will wait for all of them to finish

    • José A. Esquivel

      Elegant solution. Thanks. I ended up using your first bin, and refactoring the negation into a Promise.not function.

      • Philippe Hébert

        Thanks! Pleasure’s mine 🙂

    • Vijay

      Such a simple and elegant solution. Thanks a lot!