then from ejemplo catch await async all javascript asynchronous promise throw es6-promise

javascript - from - ¿Por qué no puedo tirar dentro de un controlador Promise.catch?



promise.all typescript (6)

Como otros han explicado, el "agujero negro" se debe a que arrojar dentro de un .catch continúa la cadena con una promesa rechazada, y no tiene más capturas, lo que lleva a una cadena sin terminar, que se traga los errores (¡malo!)

Agregue una captura más para ver qué sucede:

do1().then(do2).catch(function(err) { //console.log(err.stack); // This is the only way to see the stack throw err; // Where does this go? }).catch(function(err) { console.log(err.stack); // It goes here! });

Una captura en el medio de una cadena es útil cuando desea que la cadena continúe a pesar de un paso fallido, pero un relanzamiento es útil para continuar fallando después de hacer cosas como el registro de información o los pasos de limpieza, tal vez incluso alterando qué error es aventado.

Truco

Para hacer que el error aparezca como un error en la consola web, como originalmente pretendías, utilizo este truco:

.catch(function(err) { setTimeout(function() { throw err; }); });

Incluso los números de línea sobreviven, por lo que el enlace en la consola web me lleva directamente al archivo y la línea donde ocurrió el error (original).

Por que funciona

Cualquier excepción en una función llamada como cumplimiento de promesa o controlador de rechazo se convierte automáticamente en un rechazo de la promesa que se supone que debe devolver. El código de promesa que llama a su función se encarga de esto.

Una función llamada por setTimeout, por otro lado, siempre se ejecuta desde el estado estable de JavaScript, es decir, se ejecuta en un nuevo ciclo en el bucle de eventos de JavaScript. Las excepciones no están atrapadas por nada y llegan a la consola web. Dado que err contiene toda la información sobre el error, incluida la pila original, el archivo y el número de línea, aún se informa correctamente.

¿Por qué no puedo simplemente lanzar un Error dentro de la devolución de llamada y dejar que el proceso maneje el error como si estuviera en cualquier otro ámbito?

Si no hago console.log(err) no se imprime nada y no sé nada de lo que sucedió. El proceso acaba de terminar ...

Ejemplo:

function do1() { return new Promise(function(resolve, reject) { throw new Error(''do1''); setTimeout(resolve, 1000) }); } function do2() { return new Promise(function(resolve, reject) { setTimeout(function() { reject(new Error(''do2'')); }, 1000) }); } do1().then(do2).catch(function(err) { //console.log(err.stack); // This is the only way to see the stack throw err; // This does nothing });

Si las devoluciones de llamada se ejecutan en el hilo principal, ¿por qué el Error es tragado por un agujero negro?


De acuerdo con las especificaciones (ver 3.III.d) :

re. Si llama, arroja una excepción e,
a. Si se ha llamado a resolverPromesa o rechazarPromesa, ignórela.
si. De lo contrario, rechace la promesa con e como la razón.

Eso significa que si arroja una excepción en la función, se detectará y su promesa será rechazada. catch no tiene sentido aquí, es solo un atajo para .then(null, function() {})

Supongo que desea registrar rechazos no controlados en su código. La mayoría de las bibliotecas de promesas unhandledRejection un unhandledRejection por ello. Aquí está la esencia relevante con la discusión al respecto.


Sé que esto es un poco tarde, pero me encontré con este hilo, y ninguna de las soluciones fue fácil de implementar para mí, así que se me ocurrió la mía:

Agregué una pequeña función auxiliar que devuelve una promesa, así:

function throw_promise_error (error) { return new Promise(function (resolve, reject){ reject(error) }) }

Luego, si tengo un lugar específico en cualquiera de mi cadena de promesas donde quiero lanzar un error (y rechazar la promesa), simplemente regreso de la función anterior con mi error construido, así:

}).then(function (input) { if (input === null) { let err = {code: 400, reason: ''input provided is null''} return throw_promise_error(err) } else { return noterrorpromise... } }).then(...).catch(function (error) { res.status(error.code).send(error.reason); })

De esta manera tengo el control de lanzar errores adicionales desde dentro de la cadena de promesa. Si desea manejar también los errores de promesa ''normales'', expandiría su captura para tratar los errores ''autoejecutables'' por separado.

Espero que esto ayude, ¡es mi primera respuesta de !


Sí, promete errores de deglución, y solo puede atraparlos con .catch , como se explica con más detalle en otras respuestas. Si está en Node.js y desea reproducir el comportamiento de throw normal, imprimir el seguimiento de la pila en la consola y salir del proceso, puede hacerlo

... throw new Error(''My error message''); }) .catch(function (err) { console.error(err.stack); process.exit(0); });


setTimeout() método setTimeout() detallado anteriormente ...

.catch(function(err) { setTimeout(function() { throw err; }); });

Molesto, encontré que esto era completamente inestable. Debido a que arroja un error asincrónico, no puede envolverlo dentro de una declaración try/catch , porque la catch habrá dejado de escuchar cuando se produce el error.

Volví a usar un oyente que funcionó perfectamente y, debido a que es la forma en que se debe usar JavaScript, fue altamente comprobable.

return new Promise((resolve, reject) => { reject("err"); }).catch(err => { this.emit("uncaughtException", err); /* Throw so the promise is still rejected for testing */ throw err; });


Cosas importantes para entender aquí

  1. Ambas funciones, then y catch , devuelven nuevos objetos de promesa.

  2. Ya sea arrojando o rechazando explícitamente, se moverá la promesa actual al estado rechazado.

  3. Desde then y catch devolver nuevos objetos de promesa, se pueden encadenar.

  4. Si arroja o rechaza dentro de un controlador de promesa ( then o lo catch ), se manejará en el siguiente controlador de rechazo por el camino de encadenamiento.

  5. Como mencionó jfriend00, los manejadores then y catch no se ejecutan sincrónicamente. Cuando un controlador lanza, llegará a su fin de inmediato. Por lo tanto, la pila se desenrollará y se perderá la excepción. Es por eso que lanzar una excepción rechaza la promesa actual.

En su caso, está rechazando dentro de do1 arrojando un objeto Error . Ahora, la promesa actual estará en estado rechazado y el control se transferirá al siguiente controlador, que es then en nuestro caso.

Como el controlador de then no tiene un controlador de rechazo, el do2 no se ejecutará en absoluto. Puede confirmar esto usando console.log dentro de él. Dado que la promesa actual no tiene un controlador de rechazo, también se rechazará con el valor de rechazo de la promesa anterior y el control se transferirá al siguiente controlador que es catch .

Como catch es un controlador de rechazo, cuando haces console.log(err.stack); dentro de él, puede ver el seguimiento de la pila de errores. Ahora, está lanzando un objeto Error para que la promesa devuelta por catch también esté en estado rechazado.

Como no ha adjuntado ningún controlador de rechazo a la catch , no puede observar el rechazo.

Puedes dividir la cadena y entender esto mejor, así

var promise = do1().then(do2); var promise1 = promise.catch(function (err) { console.log("Promise", promise); throw err; }); promise1.catch(function (err) { console.log("Promise1", promise1); });

La salida que obtendrás será algo así como

Promise Promise { <rejected> [Error: do1] } Promise1 Promise { <rejected> [Error: do1] }

Dentro del controlador de catch 1, está obteniendo el valor del objeto de promise como rechazado.

Del mismo modo, la promesa devuelta por el controlador de catch 1 también se rechaza con el mismo error con el que se rechazó la promise y la estamos observando en el segundo controlador de catch .