How do you implement a "raceToSuccess" helper, given a list of promises?

You can write this quite easily yourself.

function raceToSuccess(promises) {
  return new Promise(
    resolve => 
      promises.forEach(
        promise => 
          promise.then(resolve)
      )
  );
}

This kicks off all the promises, and when any succeeds resolves the new promise with its value. Failed promises are ignored. Subsequent successful promises cause nothing to happen, since the new promise has already been resolved. Note that the resulting promise will never resolve or reject if none of the input promises resolve.

Here is a modified version which returns a rejected promise if all of the input promises reject:

function raceToSuccess(promises) {
  let numRejected = 0;

  return new Promise(
    (resolve, reject) => 
      promises.forEach(
        promise => 
          promise . 
            then(resolve) .
            catch(
              () => {
                if (++numRejected === promises.length) reject(); 
              }
           )
       )
  );
}

I like @loganfsmyth's approach; you should probably upvote it for its conceptual clarity. Here's a variation of it:

function invertPromise(promise) {
  return new Promise(
    (resolve, reject) => 
      promise.then(reject, resolve)
  );
}

function raceToSuccess(promises) {
  return invertPromise(
    Promise.all(
      promises.map(invertPromise)));
}

Another idea is to turn the failed promises into promises which neither resolve nor reject (in other words, are permanently pending), then use Promise.race:

function pendingPromise()      { return new Promise(() => { }); }
function killRejected(promise) { return promise.catch(pendingPromise); }

function raceToSuccess(promises) {
  return Promise.race(promises.map(killRejected));
}

You may or not like the behavior of this. The returned promise will never fulfill or reject if none of the input promises fulfill. It's also possible that the permanently pending promises will not get GC'd, or some engines might eventually complain about them.


This is a classic example where inverting your logic makes it much clearer. Your "race" in this case is that you want your rejection behavior to in fact be success behavior.

function oneSuccess(promises){
  return Promise.all(promises.map(p => {
    // If a request fails, count that as a resolution so it will keep
    // waiting for other possible successes. If a request succeeds,
    // treat it as a rejection so Promise.all immediately bails out.
    return p.then(
      val => Promise.reject(val),
      err => Promise.resolve(err)
    );
  })).then(
    // If '.all' resolved, we've just got an array of errors.
    errors => Promise.reject(errors),
    // If '.all' rejected, we've got the result we wanted.
    val => Promise.resolve(val)
  );
}

I'm using a function based on Promise.race() but with a twist: it ignores rejects, unless all given promises reject:

// ignores any rejects except if all promises rejects
Promise.firstResolve = function (promises) {
    return new Promise(function (fulfil, reject) {
        var rejectCount = 0;
        promises.forEach(function (promise) {
            promise.then(fulfil, () => {
                rejectCount++;
                if(rejectCount == promises.length) {
                    reject('All promises were rejected');
                } 
            });
        });
    });
};

It's based on Rich Harris's Promise polyfill race method. I just made the looping promise reject conditional: it only rejects the main promise, if all given promises failed, otherwise it ignores rejects and resolves the first success.

Usage:

// fastest promise to end, but is a reject (gets ignored)
var promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("foo")
    }, 100);
})

// fastest promise to resolve (wins the race)
var promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("bar")
    }, 200);
})

// Another, slower resolve (gets ignored)
var promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("baz")
    }, 300);
})

Promise.firstResolve([promise1, promise2, promise3])
    .then((res) => {
        console.log(res) // "bar"
    })
    .catch(err => {
        console.log(err) // "All promises were rejected" (if all promises were to fail)
    })

The reason I use this instead of the promise inverting approach, is because in my opinion this is more readable.

To please the question in the strictest way, below there is a version that resolves the first successful promise but doesn't do anything if all given promises fail:

// ignores any and all rejects
Promise.firstResolve = function (promises) {
    return new Promise(function (fulfil) {
        promises.forEach(function (promise) {
            promise.then(fulfil, () => {});
        });
    });
};

(usage same as above)

Edit: This is in fact the same as @user663031's suggestion. Which I haven't realized until just now.


Is there something in the API that permits a "raceToSuccess" kind of behavior

There is now. There is a finished Stage 4 proposal for Promise.any:

Promise.any() takes an iterable of Promise objects and, as soon as one of the promises in the iterable fulfills, returns a single promise that resolves with the value from that promise.

So, the following syntax can be used:

// assume getApi returns a Promise

const promises = [
  getApi('url1'),
  getApi('url2'),
  getApi('url3'),
  getApi('url4'),
];
Promise.any(promises)
  .then((result) => {
    // result will contain the resolve value of the first Promise to resolve
  })
  .catch((err) => {
    // Every Promise rejected
  });

Promise.any has been implemented in all modern browsers. There are some polyfills available too.