How to implement a timeout for JavaScript Promises

It's quite usual that some request or code takes too long to resolve or reject in the case of Promises in JavaScript, this involves that sometimes we won't be able to wait for the request to resolve as it may never be resolved and therefore the promise will never be fulfilled, forcing us to wait indefinitely.

By default, the Promises in JavaScript don't offer any way to cancel a promise if it's not fulfilled in X time. Fortunately, in case that you find yourself in such scenario, there are some implementations that you may use to solve this problem and I will explain them to you in this short article.

Helper method Promises.timeout

The first solution involves the Promises.race method. This built-in method returns the result (or the rejection) of the first promise that is fulfilled, while the others will be ignored. In the case of a timeout-like implementation, you may implement something like this:

// 1. Your original promise that runs custom logic
let MyPromise = new Promise(function(resolve){
    setTimeout(function() { 
        resolve('My Promise has been fulfilled in 1 second! But the timeout is set to 500ms, you will never see this :3'); 
    }, 1000);
});

// 2. The timeout promise that will impose as limit 500 milliseconds
let timeout = new Promise(function(resolve, reject){
    setTimeout(function() { 
        reject('Time out! Your promise couldnt be fulfilled in half second :c'); 
    }, 500);
});

// 3. Race both promises, if MyPromise isn't fulfilled in 500 milliseconds, the second promise will be executed and rejected.
// It will output: Time out! Your promise couldn't be fulfilled in half second :c
Promise.race([MyPromise, timeout]).then(function(data){
    console.log(data);
}).catch(function(e){
    console.log(e);
});

For convenience, if you already have some code written, you may extend the Promise prototype and create the timeout wrapper that expects as first argument your original promise and as second argument, the limit of time in milliseconds for the promise to fulfill:

/**
 * The timeoutPromise helper allows you to wrap any promise to fulfill within a timeout.
 * 
 * @param {Promise} promise A promise instance
 * @param {BigInteger} timeoutInMilliseconds The time limit in milliseconds to fulfill or reject the promise.
 * @returns {Promise} A pending Promise
 */
Promise.timeout = function(promise, timeoutInMilliseconds){
    return Promise.race([
        promise, 
        new Promise(function(resolve, reject){
            setTimeout(function() {
                reject("timeout");
            }, timeoutInMilliseconds);
        })
    ]);
};

It could be used like this:

// 1. Store your promise in a variable for readability
let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve("Success!");
    }, 4000);
});

// 2. Provide your promise as first argument of the helper function
// and provide as second parameters the time limit for this promise to be fulfilled
// in milliseconds (e.g. 3000 = 3 seconds)
Promise.timeout(myPromise, 3000).then(function(result){
    // My promise fulfilled in less than 3 seconds
    // If fulfilled, it should output: success!
    console.log(result);
}).catch(function(err){

    // 3. In this example, the timeout will be triggered as there's a limit of 3 seconds to run
    // a promise that will be fulfilled in 4 seconds.
    if(err == "timeout"){
        console.log("My promise couldn't be fulfilled in less than 3 seconds :c");
        return;
    }

    // Handle reject logic of your original promise if any
});

Alternatively if you want to use it with async and await:

// 1. Store your promise in a variable for readability
let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve("Success!");
    }, 4000);
});

// 2. An example of how to use the timeout with some code that uses await and async
async function runLogic(){    
    try{
        const result = await Promise.timeout(myPromise, 3000);

        // If fulfilled, outputs: Success!
        console.log(result);
    }catch(e){
        if(e == "timeout"){
            console.log("My promise couldn't be fulfilled in less than 3 seconds :c");
        }
    }
}

runLogic();

Happy coding ❤️!

This could interest you

Become a more social person