Es fácil olvidarse de usar try/catch en una función asíncrona o no detectar todos los posibles errores cuando se trabaja con promesas. Esto puede causar una "espera" interminable si la Promesa nunca se resuelve ni se rechaza.
¿Hay alguna forma (como a través de un proxy o alterando el constructor de promesas) para hacer que se rechace una función asíncrona u otras promesas si hay un error no detectado? A continuación se muestra un caso generalizado. Estoy buscando alguna forma de superar la "espera" (como en "p" debe rechazarse cuando se produce el error) sin corregir "badPromise".
async function badPromise() { const p = new Promise((res) => { delayTimer = setTimeout(() => { console.log('running timeout code...'); if (1 > 0) throw new Error('This is NOT caught!'); // prevents the promise from ever resolving, but may log an error message to the console res(); }, 1000); }); return p; } (async () => { try { console.log('start async'); await badPromise(); console.log('Made it to the end'); // never get here } catch (e) { console.error('Caught the problem...', e); // never get here } })();```Las promesas ya se rechazan en el caso de un error sincrónico no detectado:
Si se arroja un error en el ejecutor, la promesa se rechaza.
onFulfilled y onRejected , como en then y catchSi una función de controlador: [...] arroja un error, la promesa devuelta por
thense rechaza con el error arrojado como su valor.
asyncValor devuelto: una
Promiseque se resolverá con el valor devuelto por la función asíncrona, o se rechazará con una excepción lanzada o no detectada dentro de la función asíncrona.
Su problema aquí no es que Promise no maneje errores no detectados, es fundamentalmente porque su error es asíncrono : en lo que respecta a Promise, su función de ejecución es una pequeña función exitosa que llama a setTimeout . En el momento en que su controlador setTimeout se ejecuta y falla, lo hace con su propia pila que no está relacionada con el objeto Promise o su función; no existe nada relacionado con badPromise o p dentro de su controlador setTimeout que no sea la referencia res que el controlador incluye a través del cierre. Al igual que en la pregunta " Manejar el error de setTimeout ", las técnicas para detectar errores en los controladores de setTimeout implicaron editar o ajustar el controlador, y según la especificación HTML para temporizadores , paso 9.2, no hay oportunidad de detectar o intercalar un caso de error para la invocación. de la función pasada a setTimeout .
Aparte de editar badPromise , no hay casi nada que puedas hacer.
(La única alternativa que se me ocurre es modificar/sobrescribir tanto el constructor Promise como el método setTimeout en secuencia, envolviendo el método del constructor Promise para guardar los parámetros de resolve / reject y luego envolviendo el método setTimeout global para envolver el controlador setTimeout con el try / catch que invoca el parámetro de reject recién guardado. Debido a la fragilidad de cambiar ambos servicios globales, desaconsejo soluciones como esta).
El problema subyacente es que las devoluciones de llamada del temporizador se ejecutan como código de nivel superior y la única forma de detectar errores en ellas es escuchar eventos de error globales. Aquí hay un ejemplo del uso de un controlador global para detectar tales errores, pero tiene problemas que discutiré debajo del código:
"use strict"; let delayTimer; // declare variable async function badPromise() { const p = new Promise((res) => { let delayTimer = setTimeout(() => { // declare variable!!! console.log('running timeout code...'); if (1 > 0) throw new Error('This is NOT caught!'); // prevents the promise from ever resolving, but may log an error message to the console res(); }, 1000); }); return p; } (async () => { let onerror; let errorArgs = null; let pError = new Promise( (res, rej)=> { onerror = (...args) => rej( args); // error handler rejects pError window.addEventListener("error", onerror); }) .catch( args => errorArgs = args); // Catch handler resolves with error args // race between badPromise and global error await Promise.race( [badPromise(), pError] ); window.removeEventListener("error", onerror); // remove global error handler console.log("Made it here"); if( errorArgs) { console.log(" but a global error occurred, arguments array: ", errorArgs); } })();Problemas
addEventListener ; puede obtener diferentes argumentos si usa window.onerror = errorHandler .window del ejemplo. No es necesario que se haya generado en la llamada badPromise() .badPromise están activas al mismo tiempo, la captura de errores globales no le indicará qué llamada badPromise error. Por lo tanto badPromise es realmente malo y debe manejarse con guantes de seda. Si realmente no puede arreglarlo, es posible que deba asegurarse de que solo tenga una llamada pendiente y que no esté haciendo nada más que pueda generar un error global al mismo tiempo. No puedo comentar si esto es posible en su caso.
Alternativa
Una alternativa más genérica puede ser iniciar un temporizador antes de llamar a badPromise y usarlo para agotar el tiempo de espera del estado pendiente de la promesa devuelta;
let timer; let timeAllowed = 5000; let timedOut = false; let timeout = new Promise( res => timer = setTimeout(res, timeAllowed)) .then( timedOut = true); await Promise.race( [badPromise(), timeout]) clearTimer( timer); console.log( "timed out: %s", timedOut);Puede haber una manera de hacer esto, pero en su caso, creo que realmente desea usar la función de reject dentro de su Promesa en lugar de throw . Eso es realmente para lo que es el rechazo.
async function badPromise() { const p = new Promise((res, reject) => { delayTimer = setTimeout(() => { console.log('running timeout code...'); if (1 > 0) { reject('This is NOT caught!'); return; } res(); }, 1000); }); return p; } (async () => { try { console.log('start async'); await badPromise(); console.log('Made it to the end'); // never gets here } catch (e) { console.error('Caught the problem...', e); // should work now } })();