Search code examples
javascriptnode.jsecmascript-6es6-promise

Promise.race() returning resolved promise instead of first rejected promise


When I run the following code, it Outputs 'Promise 1' even though the Promise that should settle first in the race is promise2 and should Output 'Error Promise 2'. What is the logic behind the output coming as 'Promise 1'?

As per the mdn docs, "...it fulfills if the first promise to settle is fulfilled, and rejects if the first promise to settle is rejected."

try{
    // Promise 1
    const promise1 = new Promise((resolve, reject) => {
        setTimeout(() => resolve('Promise 1'), 2000);
    });
    // Promise 2
    const promise2 = new Promise((resolve, reject) => {
        setTimeout(() => reject('Error Promise 2'), 2000);
    });

    // Promise 3
    const promise3 = new Promise((resolve, reject) => {
        setTimeout(() => reject('Error Promise 3'), 2000);
    });

    const result = await Promise.race( [promise2, promise1, promise3]);
    console.log(result);

} catch(err) {
    console.log(err.message);
}

It Outputs : Promise 1

As per the mdn docs, about Promise.race, it states that:

It's useful when you want the first async task to complete, but do not care about its eventual state (i.e. it can either succeed or fail).


Solution

  • I think I see where the confusion is. By "first," the MDN docs mean first in time order, not the order of the promises in the array you give Promise.race. So it doesn't matter that promise2 is the first element of the array, because when you call Promise.race, none of those promises has settled yet.

    In your code, the first promise (time-wise) to settle is Promise 1, because you're scheduling three timeouts in a row, all of them with a timeout of 2000ms, so the first one scheduled is the first one that gets called, thereby resolving its promise first (time-wise). The first one to get scheduled is the timer for promise1, because the function you give new Promise (the promise executor function) is called synchronously during the new Promise call.

    (async () => {
        try {
            // Promise 1
            const promise1 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("resolving promise1");
                    resolve("Promise 1");
                }, 2000);
            });
            // Promise 2
            const promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("rejecting promise2");
                    reject("Error Promise 2");
                }, 2000);
            });
    
            // Promise 3
            const promise3 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("rejecting promise3");
                    reject("Error Promise 3");
                }, 2000);
            });
    
            const result = await Promise.race([promise2, promise1, promise3]);
            console.log(result);
        } catch (err) {
            console.log(err.message);
        }
    })();

    The order of the promises in the array you give Promise.race isn't usually relevant to which result it gives you. It only matters in the case where the promises have already settled before you call Promise.race. In that situation, yes, it would be the first one in the array.

    (async () => {
        try {
            const promise1 = Promise.resolve("Promise 1");
            const promise2 = Promise.reject("Error Promise 2");
            const promise3 = Promise.reject("Error Promise 3");
    
            // The promises have already settled before we call Promise.race
            const result = await Promise.race([promise2, promise1, promise3]);
            console.log(result);
        } catch (err) {
            console.log(err);
        }
    })();