Cómo implementar un tiempo limite de ejecución a las promesas de JavaScript

Es bastante habitual que alguna solicitud o código tarde en resolverse o rechazarse en el caso de Promesas en JavaScript, esto implica que en ocasiones no podremos esperar a que se resuelva la solicitud ya que puede que nunca se resuelva y por lo tanto la promesa nunca se cumplirá, lo que nos obligará a esperar indefinidamente.

De forma predeterminada, las promesas en JavaScript no ofrecen ninguna forma de cancelar una promesa si no se cumple en X tiempo. Afortunadamente, en caso de que te encuentres en tal escenario, existen algunas implementaciones que puedes usar para resolver este problema y te las explicaré en este breve artículo.

Método de ayuda Promises.timeout

La primera solución involucra el método Promises.race. Este método integrado devuelve el resultado (o el rechazo) de la primera promesa que se cumple, mientras que las demás se ignorarán. En el caso de una implementación similar a un tiempo de espera, puedes implementar algo como esto:

// 1. Tu promesa original que ejecuta lógica personalizada
let MyPromise = new Promise(function(resolve){
    setTimeout(function() { 
        resolve('¡Mi promesa se ha cumplido en 1 segundo! Pero el tiempo de espera está establecido en 500 ms, nunca verás esto: 3'); 
    }, 1000);
});

// 2. La promesa de timeout que impondrá como límite 500 milisegundos
let timeout = new Promise(function(resolve, reject){
    setTimeout(function() { 
        reject('¡Se acabó el tiempo! Tu promesa no se pudo cumplir en medio segundo :c'); 
    }, 500);
});

// 3. Compite con ambas promesas, si MyPromise no se cumple en 500 milisegundos, la segunda promesa se ejecutará y rechazará.
// Saldrá: ¡Se acabó el tiempo! Tu promesa no se pudo cumplir en medio segundo :c
Promise.race([MyPromise, timeout]).then(function(data){
    console.log(data);
}).catch(function(e){
    console.log(e);
});

Por conveniencia, si ya tienes algún código escrito, puede extender el prototipo de Promise y crear el contenedor de tiempo de espera que espera como primer argumento su promesa original y como segundo argumento, el límite de tiempo en milisegundos para que se cumpla la promesa:

/**
 * El ayudante timeoutPromise te permite envolver cualquier promesa a cumplir dentro de un tiempo de espera.
 * 
 * @param {Promise} promise Una instancia de promesa
 * @param {BigInteger} timeoutInMilliseconds El límite de tiempo en milisegundos para cumplir o rechazar la promesa.
 * @returns {Promise} Una promesa pendiente
 */
Promise.timeout = function(promise, timeoutInMilliseconds){
    return Promise.race([
        promise, 
        new Promise(function(resolve, reject){
            setTimeout(function() {
                reject("timeout");
            }, timeoutInMilliseconds);
        })
    ]);
};

Podría usarse así:

// 1. Guarda tu promesa en una variable para facilitar la lectura
let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve("Success!");
    }, 4000);
});

// 2. Proporciona tu promesa como primer argumento de la función auxiliar
// y proporcionar como segundo parámetro el límite de tiempo para que se cumpla esta promesa
// en milisegundos (e.g. 3000 = 3 segundos)
Promise.timeout(myPromise, 3000).then(function(result){
    // Mi promesa cumplida en menos de 3 segundos
    // Si se cumple, debería dar como resultado: ¡éxito!
    console.log(result);
}).catch(function(err){

    // 3. En este ejemplo, el tiempo de espera se activará ya que hay un límite de 3 segundos para ejecutar
    // una promesa que se cumplirá en 4 segundos.
    if(err == "timeout"){
        console.log("Mi promesa no se pudo cumplir en menos de 3 segundos :c");
        return;
    }

    // Manejar la lógica de rechazo de su promesa original, si la hubiera
});

Alternativamente, si deseas usarlo con async y await:

// 1. Guarda tu promesa en una variable para facilitar la lectura
let myPromise = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve("Success!");
    }, 4000);
});

// 2. Un ejemplo de cómo usar el tiempo de espera con algún código que usa await y async
async function runLogic(){    
    try{
        const result = await Promise.timeout(myPromise, 3000);

        // Si se cumple, imprime: ¡Éxito!
        console.log(result);
    }catch(e){
        if(e == "timeout"){
            console.log("Mi promesa no se pudo cumplir en menos de 3 segundos :c");
        }
    }
}

runLogic();

Que te diviertas ❤️!

Esto podria interesarte

Conviertete en un programador más sociable