With Promise.race(), what happens to losing promises?
Asked Answered
U

2

5

Promise.race(promise1, promise2, ...) returns a promise with the resolve/reject result for the "fastest" promise from the list.

What happens to the other promises, i.e. those that "lose" the race?

Testing with Node.js seems to indicate that they continue running.

This seems consistent with the fact that there is no way to "kill" a promise that I know of.

Is this correct ?

Undervest answered 19/5, 2021 at 13:26 Comment(3)
Don't know of too many races where the runners stop when someone wins. Just sayin'.Elaterid
Correct. Because promises don't "run". They are just markers for some async operation. The only thing you can get from a promise is the end result of the operation - you cannot affect the operation.Hungerford
Does this answer your question? Is it a documented behavior that Promise.all and Promise.race effectively make all promises "handled"?Incredible
M
10

All promises in a race will continue running even after the first one crosses the finish line -

const sleep = ms =>
  new Promise(r => setTimeout(r, ms))

async function runner (name) {
  const start = Date.now()
  console.log(`${name} starts the race`)
  await sleep(Math.random() * 5000)
  console.log(`${name} finishes the race`)
  return { name, delta: Date.now() - start }
}

const runners =
  [ runner("Alice"), runner("Bob"), runner("Claire") ]

Promise.race(runners)
  .then(({ name }) => console.log(`!!!${name} wins the race!!!`))
  .catch(console.error)
  
Promise.all(runners)
  .then(JSON.stringify)
  .then(console.log, console.error)
Alice starts the race
Bob starts the race
Claire starts the race
Claire finishes the race
!!!Claire wins the race!!!
Alice finishes the race
Bob finishes the race
[ 
  {"name":"Alice","delta":2158},
  {"name":"Bob","delta":4156},
  {"name":"Claire","delta":1255}
]
Micronucleus answered 21/5, 2021 at 4:51 Comment(2)
Is there are way to stop Alice and Bob from running once Claire wins the race?Hypozeugma
@AbdullahKhilji using native Promise, not at this time. See this Q&A for details. The short answer is you have to use a 3rd-party promise or implement a promise that is capable of this behavior.Micronucleus
W
0

The race metaphor must not be taken too literally, but you can think of it as follows:

  • The runner is an asynchronous process (like fetching a document from a web server or database, awaiting user input, or waiting for time to pass).
  • The promise is a time-taker that reports when the runner crosses the finishing line (when the document or user input becomes available or the time has passed).
  • The race winner is determined when the first time-taker reports the finishing result (aka winning time).

Even when a race has been won, the other time-takers involved (other than the one reporting the winning time) must continue watching their runners1, because they may be taking part in another race that they might still win. See the following example.

var over;

function runner(ms) {
  return new Promise(function timetaker(resolve, reject) {
    setTimeout(function() {
      if (!over) resolve(`Finished after ${ms} ms`);
    }, ms);
  });
}

function endofrace(result) {
  console.log(result);
  //over = true;
}
var veryslow = runner(2000);
var slow = runner(1000);
var fast = runner(500);
Promise.race([slow, fast]).then(endofrace);
Promise.race([slow, veryslow]).then(endofrace);

If you want to avoid that behavior ("disqualify losers from other races"), you must program "race-is-over" awareness into the promise, like the over variable in the example. If you bring in the commented-out line, the 1000 ms racer will no longer be able to win the second race.

(More precisely, if over = true the runner setTimeout will still finish, but the time-taker will not report any more. Race-is-over awareness is with the time-taker, not the runner. But in the case of a web server request, race-is-over awareness could instead be with the runner so that the request is aborted when the race is over.)


1 Another weakness of the metaphor: It is wrong to assume that the time-takers (promises) consume any resources while watching the runners.

Welterweight answered 26/9 at 15:26 Comment(3)
A promise does not "run" anything. It just gets resolved by some process, and indeed that process is unaware to whether the promise was used in a race (or none, or multiple).Roentgenograph
In your example, the setTimeout will finish even if over is true.Jersey
Yes, but the time-taker will not report in that case. That's why I differentiated between runner and time-taker.Gummite

© 2022 - 2024 — McMap. All rights reserved.