Sam H
Sam H

Reputation: 561

Promise.race continuing to run after one promise rejected

Isn't promise.race supposed to terminate all promises when one of them have rejected/resolved?

var cancelDeferred = new cancellation()

Promise.race([cancelDeferred.promise, new Promise( (res, req) => {
	setTimeout(() =>{
		console.log("hey")
	}, 2000)

})]).catch(e =>{
	console.log(e)
})

setTimeout(() => {cancelDeferred.cancel()}, 500);


function cancellation () {
    const token = {};
  
    token.promise = new Promise((_, reject) => {
      token.cancel = () => reject(new Error('cancelled'));
    });
  
    return token;
  }

In this code, one promise is cancelled after 500ms, so that should result in .catch to console log right?

No, it still logs "hey", why is this?

Upvotes: 2

Views: 1423

Answers (1)

trincot
trincot

Reputation: 350262

This is normal behaviour. It is not because a Promise rejects, that a setTimeout will be cancelled. Promise.race provides you with a promise that rejects as soon as any of the given promises rejects (or fulfils if all of them fulfil). It does not interrupt any asynchronous code that might still be pending. After the Promise.race-returned promise settles, it will become ignorant of any settlement of any of the other promises that were passed to it, but will not prevent asynchronous code execution.

setTimeout has its own "contract" that can only be broken if you call clearTimeout. No promise rejection can prevent a setTimeout callback to be called.

To make it work, you need a call to clearTimeout:

var cancelDeferred = cancellation()
var clearDeferred = delay(2000);

Promise.race([cancelDeferred.promise, clearDeferred.promise]).catch(e =>{
    console.log("catch", e.message)
    clearDeferred.clear();
})

setTimeout(() => {cancelDeferred.cancel()}, 500);


function cancellation () {
    const token = {};
  
    token.promise = new Promise((_, reject) => {
        token.cancel = () => reject(new Error('cancelled'));
    });
    return token;
}

function delay(ms) {
    const token = {};

    token.promise = new Promise((resolve) => {
        const timer = setTimeout(() => {
            resolve(); // promise makes sense only if there is this call
            console.log("hey");
        }, ms);
        token.clear = () => {
            clearTimeout(timer);
            console.log("timer cleared");
        }
    });
    return token;
}

Upvotes: 3

Related Questions