I have a node.js script that reads in some JS code from a file, and passes it to an eval()
. The code that passes the javascript to the eval is as follows:
// read javascript code from file
var outputbuffer = '';
function output(data) {
outputbuffer = data '\n';
}
eval(javascriptCodeFromFile);
// do stuff with outputbuffer
The javascript code in the file is as follows:
var fs = require('fs');
fs.readFile('myfile.txt', function(err, data) {
if (err) {
output('Error reading file');
}
else {
output(data.toString());
}
});
The problem is that the eval exits before the file read completes, as the file read is asyncronous. The eval simply starts the file read, and then exits, not waiting from the file read to complete. How can I make the eval wait for the callback before exiting? I have looked at promises and they seem PROMISing (pun intended) but I need someone to guide me in the right direction.
CodePudding user response:
Leaving all the downsides of eval, here is a direct answer to your question.
Create a promise for keeping the event loop of the main thread busy.
Resolve the promise from within the script being eval. The script will have access to the resolve and
reject` functions of the Promise since the evaluating script is within the scope of the promise.
let p = new Promise((resolve, reject) => eval(javascriptCodeFromFile));
p.then(()=> console.log(outputbuffer))
.catch((e) => console.log('Rejected ::' outputBuffer));
Here is a runnable example that demonstrates the main thread waiting for an asynchronous execution happens inside 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));
CodePudding user response:
It's fine to have your eval'd code return a promise.
example:
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));
It's also fine to make the Promise external to the eval
and just call the resolve
or reject
from the eval'd code:
e.g.:
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));
Or the same thing with await
and calling the async version of 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);
}
}