Recientemente, me resultó muy frustrante depurar una parte del código asíncrono de NodeJS: una excepción que pensé que definitivamente se detectaría en try..catch se estaba filtrando, lo que resultó en un error de promesa no manejado fuera de la función async_foo .
async function async_foo() { try { await some_library.async_bar('some illegal argument'); } catch (err) { console.error(err); // <- Whether this is called depends on async_bar's implementation ! } }Desde entonces, aprendí que hay docenas de formas de dispararte en el pie en async JS debido a cómo se implementa async ... await a través de Promises, pero aún así:
¿Es posible escribir su código JS asíncrono de una manera que definitivamente siempre manejará los errores del código asíncrono anidado, independientemente de cómo se implemente el código asíncrono anidado? Una solución basada en biblioteca contaría.
¿Es posible escribir su código JS asíncrono de una manera que definitivamente siempre manejará los errores del código asíncrono anidado, independientemente de cómo se implemente el código asíncrono anidado? Una solución basada en biblioteca contaría.
No, no es. El código asíncrono mal escrito puede arrojar una devolución de llamada asíncrona simple que es llamada por el bucle de eventos desde un marco de pila vacío sin que pueda capturar nada con un try/catch . He aquí un ejemplo trivial:
setTimeout(() => { throw new Error("thrown from a setTimeout callback"); }, 10);No hay forma de capturar esa excepción desde fuera de la devolución de llamada que no sea globalmente usando algo como:
process.on('uncaughtException', function(err) { // log and shut down });Pero en ese momento, no tiene contexto para lo que realmente sucedió o cómo arreglar el estado interno. Podría haber archivos o sockets abiertos, podría haber controladores de eventos en su lugar. Podría haber otros recursos asignados. Podría haber estado interno dejado en mal estado. El consejo habitual en ese momento es apagar el servidor y reiniciar.
En realidad, nunca quieres llegar aquí. Desea que los errores sean capturados en contexto por el código que sabe qué hacer para limpiar y manejar correctamente un error y, francamente, realmente no hay ningún sustituto para eso.
Afortunadamente, el código escrito correctamente usando promesas y .then() y .catch() o await y try/catch propagará las promesas rechazadas en la cadena de llamadas hasta donde usted quiera que llegue. Pero, para hacer eso, el código debe estar escrito correctamente. El código mal escrito que no encadena ni devuelve promesas aún puede crear situaciones en las que las excepciones o los rechazos no se manejen correctamente, incluso cuando se usan promesas.
¿Es posible escribir su código JS asíncrono de una manera que definitivamente siempre manejará los errores del código asíncrono anidado, independientemente de cómo se implemente el código asíncrono anidado?
No, y probablemente nunca lo habrá. La documentación de Node.js lo explica bastante bien:
Por la naturaleza misma de cómo funciona throw en JavaScript, casi nunca hay una forma segura de "continuar donde se quedó", sin filtrar referencias o crear algún otro tipo de estado frágil indefinido. La forma más segura de responder a un error lanzado es cerrar el proceso.
Depende de la biblioteca dar cuenta de cada tipo de error y manejarlos de manera segura y propagarlos (si es necesario) al consumidor cuando ocurran.
Podría usar el módulo de domain en desuso, pero probablemente no lo ayudaría en este caso porque "no se emite ningún evento 'error' para rechazos de Promesa no manejados".