DEV Community 👩‍💻👨‍💻

Cover image for Using Promise.race usefully
Gajanan Patil
Gajanan Patil

Posted on

Using Promise.race usefully

When doing long running tasks like :-

  1. DB query which may take long time
  2. Reading big files
  3. API which may take long time to complete
  4. Waiting for some event

You may want to stop if the task is taking more time than usual to get completed. In that case Promise.race can be useful.

Puppeteer project uses Promise.race to handle page/network lifecycle events with timeouts during automation.

Here's an example :-

/**
 * A utility function which throws error after timeout
 * @param timeout - timeout in seconds
 * @returns - promise which gets rejected after timeout
 */
function timer(timeout) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('❌ failed with timeout'))
        }, timeout * 1000)
    })
}

/**
 * Mock db query which take 5 seconds to complete
 * @returns - query promise
 */
function bigQuery() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 5 * 1000)
    })
}

// race both bigQuery and timer tasks
// `Promise.race` can take multiple promises if you want to race them all
Promise.race([
    bigQuery(),
    timer(1)
]).then(() => console.log('✅ Query successful within time limit'))
    .catch(console.error)

// ==> will log '❌ failed with timeout'


// also try running above code by changing timer's timeout value to 6, you will get successful log
Enter fullscreen mode Exit fullscreen mode

Promise returned by Promise.race resolve/rejects with whichever promise in array resolves/rejects first. For more info checkout MDN docs.

You can play with the above code here :-

/** A utility function which throws error after timeout @param timeout - timeout in seconds @returns - promise which gets rejected after timeout */ function timer(timeout) { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('❌ failed with timeout')) }, timeout * 1000) }) } /** Mock db query which take 5 seconds to complete @returns - query promise */ function bigQuery() { return new Promise((resolve, reject) => { setTimeout(resolve, 5 * 1000) }) } // race both bigQuery and timer tasks // Promise.race can take multiple promises if you want to race them all Promise.race([ bigQuery(), timer(1) ]).then(() => console.log('✅ Query successful within time limit')) .catch(console.error) // ==> will log '❌ failed with timeout' // also try running above code by changing timer's timeout value to 6, you will get successful log

💡 Let me know in comments other cool ideas using Promise.race

See my projects on Github.

Top comments (2)

Collapse
 
lukeshiru profile image
Luke Shiru

Ideally your function should look more like this:

const timer = timeout =>
    new Promise((_resolve, reject) =>
        setTimeout(() => reject(new Error("The Promise timed out")), timeout),
    );
Enter fullscreen mode Exit fullscreen mode

Because maybe the other promise in the array of promises rejects, and you're assuming the reject is always because of a timeout, when it could actually be something else:

Promise.race([bigQuery(), timer(1000)])
    .then(() => console.log("✅ Query successful within time limit"))
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

And one other approach you could do is make a util that makes the race for you:

const timer = timeout =>
    new Promise((_resolve, reject) =>
        setTimeout(() => reject(new Error("The Promise timed out")), timeout),
    );

const timedPromise = timeout => promise =>
    Promise.race([promise, timer(timeout)]);
Enter fullscreen mode Exit fullscreen mode

And then you can just:

timedPromise(1000)(bigQuery())
    .then(() => console.log("✅ Query successful within time limit"))
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

And because is curried, you can reuse it all you want, go crazy:

Promise.all([promiseA(), promiseB(), promiseC()].map(timedPromise(5000)))
    .then(console.log)
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
 
gajananpp profile image
Gajanan Patil • Edited on

Thanks !!!, this looks better. The utility function to race any promise with timeout is useful 👍

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post