【问题标题】:How to use await in a callback?如何在回调中使用等待?
【发布时间】:2018-08-12 03:30:41
【问题描述】:

我的代码是:

async function run() {
  await sleep(1);
  fs.readFile('list.txt', 'utf8', function (err, text) {
    console.log(text);
    await sleep(5);
   });
}

run();

await sleep(1) 很好,但 await sleep(5) 会导致:

SyntaxError: async 只在 async 函数中有效

fs.readFile 有问题吗?在这种情况下如何使用await

以上是要测试的简化示例。在我的实际使用中,我需要将await sleep 放在一个回调中,深度嵌套在几个异步函数中。

【问题讨论】:

  • 是关于你传递给readFile的第三个参数,它是一个函数。它也必须是异步的,因为您在该函数中使用了 await。
  • 尝试在readFileparameters 中的函数前添加async,如下所示:fs.readFile('list.txt', 'utf8', async function (err, text) ...
  • fs.readFile()的回调函数不是异步函数。 await 只能用于异步函数。
  • 你确定你发布了正确的错误信息吗?

标签: javascript node.js


【解决方案1】:

我不确定sleep 正在做什么,但您的问题正在发生,因为fs.readfile 的回调不是异步函数,而await sleep(5) 就是在那里。

async function run() {
    await sleep(1);
    fs.readFile('list.txt', 'utf8', function (err, text) {
    console.log(text);           /* ^this isn't an async function */
    await sleep(5);
    });
}

可以通过将异步函数作为回调传递来消除该错误,但是不建议混合使用 Promise 和回调流,这会导致问题,尤其是在处理错误时。它可以防止您从run() 之外的内部等待中链接承诺并捕获错误。

更好的方法是将fs.readFile() 包装在一个promise 中并等待它。

async function run() {
  await sleep(1);
  const text = await new Promise((resolve, reject) => {
    fs.readFile('list.txt', 'utf8', function (err, text) {
      if (err) reject(err) else resolve(text);
    });
  });
  console.log(text);
  await sleep(5);
}

这将使您能够以更强大的方式捕获任何错误:

run()
.then(() => /*...*/)
.catch(err => /* handle error */

并避免未经处理的拒绝。

【讨论】:

  • 通常我不会反对同级答案,但接受的答案是错误的。它可以说明如何在回调中使用await,但在现实生活中永远不应该这样做,也不应该向任何人推荐。这就是 UnhandledPromiseRejectionWarning 和“为什么我有与 await run() 的竞争条件”之类的问题的来源。 await sleep(5) 对初学者没有任何影响。
  • @estus,你是对的。看看你更明智的解决方案,如果可以的话,我会删除它,但由于它被选中,我不能。如果您想编辑它以使其对未来的用户正确,我不介意(我也可以这样做,但我只是在公然抄袭您。)
  • 当然,请随时更新答案,我会撤回我的投票。用户最好先看到可用的答案,这就是向大多数用户显示已接受答案的方式。
【解决方案2】:

以上是要测试的简化示例。在我的实际使用中,我需要将 await sleep 放在一个回调中,深度嵌套在几个异步函数中。

当有机会遵守承诺时,不应使用async..await 或一般承诺来扩充基于回调的 API(如果多次调用回调,则可能无法实现)。这会导致糟糕的控制流和错误处理。

一旦run 内部有async 回调函数,就不可能将嵌套的promise 与run().then(...) 链接起来。来自嵌套 Promise 的错误也可能未处理并导致 UnhandledPromiseRejectionWarning

正确的方法是从回调转移到 Promise 并使用 Promise 控制流。通常这可以通过 promise 构造函数来实现:

async function run() {
  await sleep(1);
  const text = await new Promise((resolve, reject) => {
    fs.readFile('list.txt', 'utf8', function (err, text) {
      if (err) reject(err) else resolve(text);
    });
  });
  console.log(text);
  await sleep(5);
}

numerous ways 可以从基于节点回调的 API 中获取承诺,例如fs 从 Node 10 开始提供 promise API:

async function run() {
  await sleep(1);
  const text = await fs.promises.readFile('list.txt', 'utf8');
  console.log(text);
  await sleep(5);
}

【讨论】:

    【解决方案3】:

    如果您的函数包含“await”,您必须在函数声明前加上“async”。

    注意:通过使您的函数成为“异步”函数,承诺会始终返回。因此,如果您返回一个字符串,该字符串将自动包装在一个 Promise 对象中。

    因此,您可以手动返回 Promise(为清楚起见)。一个简单的已解决承诺就足够了:

    async function foo(){
        await bar();
        return Promise.resolve('optional'); // optionally resolve with a value
    }
    

    关键字“async”总是在关键字“function”之前:

    var foo = async function(){
        await bar();
        return Promise.resolve('optional'); // optionally resolve with a value
    }
    

    ... 以及箭头函数:

    var foo = sally( async () => {
        await bar();
        return Promise.resolve('optional'); // optionally resolve with a value
    })
    

    获取返回值:

    foo().then(val => console.log(val) ); // prints "optional" to console
    

    【讨论】:

    • “通过使你的函数成为“异步”函数,你需要返回一个承诺。” async 函数总是将返回值包装在一个承诺中。所以你的例子等价于return 'optional';
    • @felix 感谢提示,Promise.resolve() 返回一个 Promise 对象,因为我们直接从本机 window.Promise 调用“resolve”方法。这只是创建简单 Promise 的一种快速、廉价的方法。
    • 我认为 Felix 的观点是您不需要在 async 函数中显式返回承诺。它不仅没有必要,而且违背了 async-await 的目的,因为 async 函数的意义在于它隐式返回一个承诺,该承诺将您返回(或抛出)的任何内容包装在函数中。
    • 同意,但新手需要注意async 函数会更改任何不是承诺的返回值。 Felix 暗示返回值是 typeof string 而不是 typeof promise
    【解决方案4】:

    您必须在功能启动前使用 aysnc 键。 等待关键字 无法正常使用。

    async function run() {
      await sleep(1);
      fs.readFile('list.txt', 'utf8', async function (err, text) {
        console.log(text);
        await sleep(5);
       });
    }
    
    run();
    

    【讨论】:

      猜你喜欢
      • 2020-01-12
      • 2018-05-16
      • 1970-01-01
      • 2021-06-03
      • 2016-09-03
      • 1970-01-01
      相关资源
      最近更新 更多