【问题标题】:await is only valid in async function - eval in asyncawait 仅在 async 函数中有效 - eval 在 async
【发布时间】:2019-10-04 19:51:45
【问题描述】:

我想 eval() 异步函数内部的一些代码行。虽然下面的代码没问题,

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

以下抛出错误:await 仅在异步函数中有效

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

我该如何处理? 我的 foo() 应该是异步的,因为它是 Puppetetteer 控制器功能

【问题讨论】:

  • 退后一步 - 为什么你想eval代码?如果你给我们真正的问题,也许它会变成there is a different solution
  • 由于条件不同,我想执行不同的动作。我的 async foo() 很大,但只有我想通过 eval() 执行的一小段代码是不同的。
  • 您已经可以执行不同的函数,而无需 eval - if 例如,或者调用完全不同的函数、多态性、设置具有功能的查找表等等。
  • 好的,我的 X 问题是我想在函数内部使用一些不同的代码,应该是异步的,最好的 Y 是什么?将所有 async() 代码移动到包含中,如答案 #1 中所述?
  • 为什么我们总是在询问 eval 时必须有人说不要使用 eval?只要回答这个问题,它就有合法的用例。

标签: javascript node.js asynchronous eval


【解决方案1】:

这是另一种无需睡觉或做任何复杂事情的方法。

在您传递给eval() 的代码中,将整个代码包装在另一个异步函数中并将其设置为某个变量,例如EVAL_ASYNC。然后在运行eval(ctxScript) 之后,运行那个异步函数await EVAL_ASYNC

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

【讨论】:

    【解决方案2】:

    如果你希望能够等待评估,你可以使用这个:

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

    这使用AsyncFunction 构造函数。 MDN 有a page on it 描述了使用它和使用eval 的区别:

    注意:使用 AsyncFunction 构造函数创建的异步函数不会为其创建上下文创建闭包;它们总是在全局范围内创建。

    在运行它们时,它们将只能访问自己的局部变量和全局变量,而不能访问调用 AsyncFunction 构造函数的范围内的变量。

    这不同于将 eval 用于异步函数表达式的代码。

    这意味着,如果您希望评估代码能够访问变量,则需要将它们添加到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"];
    

    【讨论】:

    • 您是否在异步函数中运行示例?该示例创建了一个异步函数并使用 await 调用它,因此您需要在异步函数中才能使用它。
    【解决方案3】:

    最终使用了 Ermir 的答案:

    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
    }
    

    【讨论】:

    • 在代码中睡觉从来都不是一个好主意嘿。如果您的 eval 脚本失败,那么它将永远休眠,直到您重新启动系统。如果你真的想使用睡眠,也许将所有东西都包装在一个 try catch 中,这样如果代码中断,你的睡眠就会停止。类似try {eval(ctxScript)} catch () {is_script_ended = true}
    【解决方案4】:

    如果你想在一些更大的函数中动态调用一些异步代码,那么你可以提供一个回调来为你做这件事。这样,您可以通过赋予不同的回调函数来执行不同的额外功能来调用您的函数:

    // 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();
    })

    【讨论】:

    • 非常好的例子!现在想象一下,我在异步函数 foo() 的定义中有一些对象,它们在 --- 一些代码 --- 和回调之前创建,并且有数千个不同的双重等待(现在存储到文件中),当次要时调用这个函数 foo(),想要使用这些对象。
    • ths 没有解释如何从异步中获取表达式值,只是为了稍后调用一个函数
    • @wowow 有什么问题? const result = await callback() 将获得该值。我认为不需要明确说明,因为您这样做的方式没有改变。我专注于展示如何避免eval
    【解决方案5】:

    foo() 不一定是async,因为这对eval 的执行上下文没有影响。相反,一种可能的解决方案是将您的 ctxScript 包装在一个自动执行的异步函数中,如下所示:eval("(async () =&gt; {" + ctxScript + "})()")

    【讨论】:

    • 我的 foo() 应该是异步的,因为它是 Puppetetteer 控制器功能。
    • 我的意思是 foo() 是否异步与 eval 中的代码是否在异步执行上下文中无关。因此我的回答。
    • 所以,async 函数内部的 eval() 内部的 await 不起作用。
    • 从上面的 cmets 来看,也许在这里使用 eval 并不是解决问题的最佳方法,因为它有很多缺点(速度较慢、执行上下文不同、代码注入的风险)。
    • 我明白了,eval() 现在只是作为一个快速补丁,看到并理解并接受所有风险。但这是我第一次使用 node.js 和 async,所以我还不知道更好的方法。
    猜你喜欢
    • 2018-09-01
    相关资源
    最近更新 更多