He estado estudiando mucho sobre javascript asíncrono, desde devoluciones de llamada hasta promesas de async/await.
Ahora me cuesta entender por qué pierdo el control sobre el manejo de errores en el siguiente ejemplo de código. Incluso me sumergí en el código fuente de Node, pero eso me dio más problemas que respuestas.
Esto es lo que hago:
const { readFile } = require('node:fs') process.on('uncaughtExceptionMonitor', (error, origin) => { console.log('\nuncaughtExceptionMonitor handler: {') console.log(`ERROR: ${error}`) console.log(`ORIGIN: ${origin}`) console.log('}\n') }) try { readFile('./jsconfig.json', 'utf8', (err, res) => { if (err) console.log('failed to read file') if (res) console.log('successfully read file') throw new Error(`THROWN BY BUILT-IN FUNCTION'S CALLBACK`) //error thrown }) } catch (error) { console.log(`error caught by readFile's outer scope`) // not caught here }
Al ejecutarlo me da el siguiente resultado:
archivo leído con éxito
controlador de uncaughtExceptionMonitor: { ERROR: Error: EMITIDO POR EL ORIGEN DE LA DEVOLUCIÓN DE LLAMADA DE LA FUNCIÓN INTEGRADA: uncaughtException }
C:\Users\Heavy\Documents\dev\hwoarang\leg.js:15 arrojar un nuevo error (
THROWN BY BUILT-IN FUNCTION'S CALLBACK) // error arrojado ^Error: EMITIDO POR LA DEVOLUCIÓN DE LLAMADA DE LA FUNCIÓN INTEGRADA en C:\Users\Heavy\Documents\dev\hwoarang\leg.js:15:11 en FSReqCallback.readFileAfterClose [como oncomplete] (node:internal/fs/read_file_context:68:3 )
Esta es mi primera pregunta, por lo que no me siento muy cómodo con el formato de Stackoverflow. Me disculpo por eso.
¿Me puedes ayudar?
try...catch se aplica al código que se ejecuta sincrónicamente en el bloque try . La devolución de llamada otorgada a la función readFile no se ejecuta sincrónicamente, sino después de que el bloque try haya completado la ejecución (y todo el código sincrónico que lo sigue hasta que la pila de llamadas se haya vaciado y el motor procese las colas de trabajos). Una vez que la ejecución completa el bloque de try , nunca puede suceder que se ingrese al bloque de catch más adelante.
Más tarde, se crea un nuevo contexto de ejecución donde se ejecuta la devolución de llamada. Este contexto de ejecución no sabe nada acerca de este bloque try..catch .
Puede hacer uso de la versión de promesa de fs :
const { promises: { readFile } } = require("fs"); async function test() { try { const res = await readFile('./jsconfig.json', 'utf8'); console.log('successfully read file'); throw new Error(`THROWN AFTER AWAIT`) //error thrown } catch (error) { console.log(`error caught`) // will be caught } } test(); Aquí, con await , el bloque try se suspende junto con el contexto de ejecución de la función. Una vez que se resuelve la promesa esperada, ese contexto de función se restaura con su bloque de try sin terminar, y luego la ejecución continúa dentro de ese bloque, por lo que se detectará un error y se ejecutará el bloque catch .
readFile es una operación asíncrona; por lo tanto, no se ejecutará de la manera típica paso a paso (línea por línea). Entonces, cuando se lanza el error, el código síncrono que tiene ya se habrá ejecutado.
La solución más simple aquí es usar readFileSync síncrono que se ejecutará dentro del mismo contexto de ejecución.
Si desea tener el código de aspecto síncrono mientras aún ejecuta un código asíncrono, puede prometer la función readFile y llamarla usando async/await:
const readFilePromise (filename, format) = new Promise((resolve, reject) => { readFile(filename, format, (err, res) => { if (err) { reject(err); } else { resolve(res); } }); }); // usage (async () => { try { const res = await readFilePromise('./jsconfig.json', 'utf8'); console.log('successfully read the file', res); } catch (err) { console.log('failed to read file'); } })();