Tengo un script node.js que lee un código JS de un archivo y lo pasa a un eval() . El código que pasa el javascript al eval es el siguiente:
// read javascript code from file var outputbuffer = ''; function output(data) { outputbuffer += data + '\n'; } eval(javascriptCodeFromFile); // do stuff with outputbufferEl código javascript en el archivo es el siguiente:
var fs = require('fs'); fs.readFile('myfile.txt', function(err, data) { if (err) { output('Error reading file'); } else { output(data.toString()); } });El problema es que la evaluación finaliza antes de que se complete la lectura del archivo, ya que la lectura del archivo es asíncrona. La evaluación simplemente inicia la lectura del archivo y luego sale, sin esperar a que se complete la lectura del archivo. ¿Cómo puedo hacer que la evaluación espere la devolución de llamada antes de salir? He mirado las promesas y parecen PROMETEDORAS (juego de palabras intencionado) pero necesito a alguien que me guíe en la dirección correcta.
Dejando todas las desventajas de eval, aquí hay una respuesta directa a su pregunta.
Cree una promesa para mantener ocupado el ciclo de eventos del hilo principal.
Resuelva la promesa desde el script que se está evaluando. El script tendrá acceso a las funciones de resolve and rechazo de la Promesa ya que el script de evaluación está dentro del alcance de la promesa.
let p = new Promise((resolve, reject) => eval(javascriptCodeFromFile)); p.then(()=> console.log(outputbuffer)) .catch((e) => console.log('Rejected ::' + outputBuffer));Aquí hay un ejemplo ejecutable que demuestra que el hilo principal espera que ocurra una ejecución asíncrona dentro de eval
let jsCode = ` setTimeout(() => (resolve('Hello from eval after two seconds')), 2000); `; console.log('waiting...'); let p = new Promise((resolve, reject) => { eval(jsCode); }) p.then((data)=> console.log(data));Está bien que su código evaluado devuelva una promesa.
ejemplo:
const code = ` new Promise((resolve, reject) => { var fs = require('fs'); fs.readFile('myfile.txt', function (err, data) { if (err) { reject('Error reading file'); } else { resolve(data.toString()); } }); }); `; console.log('starting...'); eval(code) .then(str => console.log(str)) .catch(err => console.error(err)); También está bien hacer que Promise sea externo a la eval y simplemente llamar a resolve o reject desde el código evaluado:
p.ej:
const code = ` var fs = require('fs'); fs.readFile('myfile.txt', function (err, data) { if (err) { reject(err); } else { resolve(data.toString()); } }); `; console.log('starting...'); new Promise((resolve, reject) => eval(code)) .then(str => console.log(str)) .catch(err => console.error(err)); O lo mismo con await y llamar a la versión asíncrona de readFile
const code = ` require('fs').promises.readFile('myfile.txt') `; console.log('starting...'); main().then(() => console.log('...done')); async function main() { try { let result = await eval(code); console.log(result); } catch(err) { console.error(err); } }