¿Por qué no puedo simplemente lanzar una Error
devolución de llamada dentro de la captura y dejar que el proceso maneje el error como si estuviera en cualquier otro ámbito?
Si no hago console.log(err)
nada, se imprime 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é Error
se traga un agujero negro?
javascript
asynchronous
promise
throw
es6-promise
demian85
fuente
fuente
.catch(…)
vuelve..catch((e) => { throw new Error() })
, escribir.catch((e) => { return Promise.reject(new Error()) })
o simplemente.catch((e) => Promise.reject(new Error()))
Respuestas:
Como otros han explicado, el "agujero negro" se debe a que arrojar dentro de una
.catch
cadena continúa con una promesa rechazada, y no tiene más capturas, lo que lleva a una cadena no terminada, que traga errores (¡malo!)Agregue una captura más para ver qué sucede:
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:
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.fuente
function logErrors(e){console.error(e)}
luego úsalo comodo1().then(do2).catch(logErrors)
. La respuesta en sí es genial por cierto, +1window.onerror
controlador de eventos. Solo haciendo elsetTimeout
truco se puede hacer esto. Dewindow.onerror
lo contrario , nunca escuchará nada sobre los errores ocurridos en Promise.console.log
opostErrorToServer
, simplemente puedes hacer lo que hay que hacer. No hay ninguna razón por la que el código que se encuentrawindow.onerror
no se pueda descomponer en una función separada y se llame desde 2 lugares. Probablemente sea incluso más corto que lasetTimeout
línea.Cosas importantes para entender aquí
Ambas funciones
then
ycatch
devuelven nuevos objetos de promesa.Ya sea arrojando o rechazando explícitamente, se moverá la promesa actual al estado rechazado.
Desde
then
ycatch
devolver nuevos objetos de promesa, se pueden encadenar.Si arroja o rechaza dentro de un controlador de promesa (
then
ocatch
), se manejará en el siguiente controlador de rechazo por el camino de encadenamiento.Como mencionó jfriend00, los controladores
then
ycatch
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
do1
arrojando unError
objeto. Ahora, la promesa actual estará en estado rechazado y el control se transferirá al siguiente controlador, que esthen
en nuestro caso.Como el
then
controlador no tiene un controlador de rechazo,do2
no se ejecutará en absoluto. Puede confirmar esto usandoconsole.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 seacatch
.Como
catch
es un controlador de rechazo, cuando lo hacesconsole.log(err.stack);
dentro de él, puedes ver el seguimiento de la pila de errores. Ahora, está arrojando unError
objeto para que la promesa devueltacatch
también esté en estado rechazado.Como no ha adjuntado ningún controlador de rechazo al
catch
, no puede observar el rechazo.Puedes dividir la cadena y entender esto mejor, así
La salida que obtendrás será algo así como
Dentro del
catch
controlador 1, está obteniendo el valor delpromise
objeto como rechazado.Del mismo modo, la promesa devuelta por el
catch
controlador 1 también se rechaza con el mismo error con el quepromise
se rechazó y la estamos observando en el segundocatch
controlador.fuente
.then()
controladores son asíncronos (la pila se desenrolla antes de que se ejecuten), por lo que las excepciones dentro de ellos deben convertirse en rechazos, de lo contrario no habría controladores de excepciones para atraparlos.Probé el
setTimeout()
método detallado anteriormente ...Molesto, encontré que esto era completamente inestable. Debido a que arroja un error asincrónico, no puede envolverlo dentro de una
try/catch
declaración, porquecatch
habrá dejado de escuchar cuando se produzca 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.
fuente
De acuerdo con las especificaciones (ver 3.III.d) :
Eso significa que si arroja una excepción en la
then
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 promete un fuego
unhandledRejection
para ello. Aquí está la esencia relevante con la discusión al respecto.fuente
unhandledRejection
enlace es para JavaScript del lado del servidor, en el lado del cliente, diferentes navegadores tienen diferentes soluciones. Todavía no lo hemos estandarizado, pero está llegando de manera lenta pero segura.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í:
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í:
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 'arrojados' por separado.
Espero que esto ayude, ¡es mi primera respuesta de stackoverflow!
fuente
Promise.reject(error)
en lugar denew Promise(function (resolve, reject){ reject(error) })
(que de todos modos necesitaría una declaración de devolución)Sí, promete errores de deglución, y solo puede detectarlos
.catch
, como se explica con más detalle en otras respuestas. Si está en Node.js y desea reproducir elthrow
comportamiento normal , imprimiendo el seguimiento de la pila en la consola y el proceso de salida, puede hacerlofuente
unhandledRejection
evento