简体   繁体   中英

await is only valid in async function - eval in async

I want to eval() some lines of code inside of async function. While the following code is ok,

async function foo()
{
  await foo1();
  await foo2();
}

the following throws an error: await is only valid in async function

let ctxScript = 'await foo1(); await foo2();';
async function foo()
{
  eval( ctxScript );
}

How could I handle this? My foo() should be async as it is Puppetteer controller function

foo() should not necessarily be async , as that has no effect on the execution context of eval . Instead, a possible solution is to wrap your ctxScript in a self-executing async function, like so: eval("(async () => {" + ctxScript + "})()")

Ended up using Ermir`s answer:

let ctxScript = '(async () => {await foo1();await foo2();is_script_ended = true; })();';

async function foo()
{
  // a lot of code
  is_script_ended = false;
  eval( ctxScript );
  while(!is_script_ended){ await sleep(1000); }
  // a lot of code
}

If you want to be able to await the eval you can use this:

await Object.getPrototypeOf(async function() {}).constructor("your code here")();

This uses the AsyncFunction constructor. MDN has a page on it which describes the differences between using it and using eval :

Note: async functions created with the AsyncFunction constructor do not create closures to their creation contexts; they are always created in the global scope.

When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the AsyncFunction constructor was called.

This is different from using eval with code for an async function expression.

This means that if you have variables that you want your evaled code to be able to access, you need to add them to globalThis :

const testVar = "Hello world";
globalThis["testVar"] = testVar;
const result = await Object.getPrototypeOf(async function() {}).constructor(`
    console.log(testVar);
    await myAsyncFunc();
    return testVar;
`)();
// result will be "Hello world"
delete globalThis["testVar"];

If you want to dynamically call some async code in some larger function, then you can supply a callback that does this for you. This way you can call your function with different extra functionality by giving it different callback functions to execute:

 // some sample async functions var resolveAfter2Seconds = function() { console.log("starting slow promise -> "); return new Promise(resolve => { setTimeout(function() { resolve("slow"); console.log("<- slow promise is done"); }, 2000); }); }; var resolveAfter1Second = function() { console.log("starting fast promise ->"); return new Promise(resolve => { setTimeout(function() { resolve("fast"); console.log("<- fast promise is done"); }, 1000); }); }; //a function that accepts a callback and would await its execution async function foo(callback) { console.log("-- some code --"); await callback(); console.log("-- some more code --"); } //calling with an async function that combines any code you want to execute foo(async () => { await resolveAfter2Seconds(); await resolveAfter1Second(); })

Here is another way without having to sleep or do anything complex.

In the code you pass to eval() , wrap your entire code in another async function and set it to some variable, for example, EVAL_ASYNC . Then after running eval(ctxScript) , run that async function await EVAL_ASYNC .

let ctxScript = 'var EVAL_ASYNC = async function() {await foo1(); await foo2();}';
async function foo()
{
  eval( ctxScript );
  await EVAL_ASYNC();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM