【问题标题】:Does async make everything inside it asynchronous?async 是否使其中的所有内容都异步?
【发布时间】:2018-12-14 13:18:36
【问题描述】:

根据 MDN,

异步函数声明定义了一个异步函数

我理解它是因为该函数将被视为异步进程,就像setTimeout 或某些数据库请求一样。例如,在下面的示例中,进程应该在数字之间的某个位置输出“main”。

let func2 = async () => {
   for (let i = 0; i < 51; i ++) {
       console.log(i);
   }
}

(async () => {
    func2();
    console.log("main");
})()

但是,“main”总是在最后得到安慰,就像整个过程是同步的。我理解错了什么?

如果同步代码还是同步的,async的目的是什么?只允许 await 在其中并提供一些花哨的方式来返回 Promise?

【问题讨论】:

  • 你是对的;添加关键字不会神奇地使代码异步。
  • JavaScript 在技术上是同步的,因为它一次只能运行一件事。 async 基本上是在告诉解析器它可以在以后运行该函数。当它运行时,这是当前运行的唯一东西medium.com/@kvosswinkel/…
  • “只允许在其中等待并提供一些花哨的方式来返回一个 Promise?” 差不多,但它非常花哨,因为它将复杂的嵌套控制语句拆分为一个承诺链.
  • Javascript 是单线程的,所以即使代码是异步的,你也不会乱序。
  • @GetOffMyLawn 我在某处看到 JavaScript 可以在进程之间切换,因此它的行为类似于多线程。这就是为什么我认为它会在主进程和异步函数之间切换,从而使两个进程并行

标签: javascript node.js async-await


【解决方案1】:

不,异步函数内部的主体在调用时会运行,但会因await 表达式而暂停。如果 await 没有任何内容,则该函数将像普通函数一样运行,但它返回一个承诺。

但是,如果存在await,则会暂停执行并继续事件循环。当等待的 promise 在当前事件循环完成后的某个时间解决时,它会从中断的地方继续。例如,比较此处记录“main”与您的示例的时间。

let func2 = async () => {
    console.log("Start")
    for (let i = 0; i < 10; i ++) {
        if(i == 5) {
            await new Promise(resolve => setTimeout(resolve, 200))
        }
        console.log(i);
    }
 }
 
 (async () => {
     func2().then(() => console.log('done'));
     console.log("main");
 })()

考虑async 函数的另一种方法是考虑它们与generators 的关系。我喜欢将await 视为一种yield。例如,我们唯一需要在 func2 的主体中更改以获得相同的行为是将 await 替换为 yield

function* func2() {
  console.log("Start")
  for (let i = 0; i < 10; i++) {
    if (i == 5) {
      yield new Promise(resolve => setTimeout(resolve, 0))
    }
    console.log(i);
  }
  return "done"
}

let gen = func2()
gen.next().value.then(() => console.log(gen.next().value))

console.log("main")

async/await 只是让这对于常见的用例来说更容易,更直观。

【讨论】:

    【解决方案2】:

    async 是否使其中的所有内容都异步?

    没有。它不是。函数中的代码仍然同步运行,并且会在运行时阻塞事件循环。

    如果同步代码仍然是同步的,那么异步的目的是什么?只允许在其中等待并提供一些奇特的方式来返回 Promise?

    是的,它主要用于await

    但是,在某些情况下,它会自动使用 Promise 包装您的代码这一事实很有用。例如,它会自动捕获任何抛出的异常并将它们转换为被拒绝的承诺。有时这很有用。有关此其他用途的示例,请参阅本文的“错误处理”部分:6 Reasons Why JavaScript’s Async/Await Blows Promises Away

    但是,async 关键字的主要功能是定义一个可以在其中使用await 的函数。这就是 ES7 的设计者决定让await 工作的方式。

    仅供参考,如果函数中有一个 await 等待承诺,那么这将导致函数在 await 处提前返回。它将在那时返回并返回一个承诺。您正在等待的操作将已经开始,并且在等待的承诺解决之前,该功能的其余部分将不会执行。因此,使用await 会使一些代码稍后执行。但是,与await 之前的代码一样,即使函数在await 解析后恢复运行,您在该函数中的Javascript 仍然是同步和阻塞的(直到另一个await 或直到return)。

    将它包装在一个 Promise 中并使用 .then() 检测它何时完成将在调用 .then() 处理程序时推迟到事件循环的下一个滴答声。所以,.then() 会稍微延迟,因为你将它包装在一个 Promise 中,然后使用了.then(),但是你的代码运行的时间不会改变。它仍然是同步和阻塞的。

    在 Javascript 中,采用同步代码并使其异步的唯一方法是:

    1. 在另一个进程中运行它并使用进程间通信来传回结果。

    2. 在本机代码插件中重写您的代码,该插件使用本机线程或其他一些操作系统异步接口来完成实际工作,然后从您的插件中为 Javascript 提供异步接口- on(通常是一个返回承诺或通过回调通知完成的接口)。这现在是 node.js 异步函数,例如 fs.readFile() 工作。他们有一个本机代码实现立即返回,然后在实现中使用本机线程并通过回调通知完成。

    3. 使用一些 node.js 插件来为你做一些事情。有些插件声称可以提供线程。

    4. 在节点版本 10.5+ 中使用 node.js 线程,并根据它们需要的限制进行编程。这是一篇关于该主题的文章:Threads in Node 10.5.0: a practical intro

    【讨论】:

    • 感谢有关 10.5 个线程的链接。首先我听说了!
    • 我也注意到了你的仅供参考。我们可以说一旦 async 函数中有一个 await,return 就没有用了吗?
    • @zhangjinzhou - 不,不是没用。 return 将设置 promise 的解析值。该函数实际上会在很久以前向调用者返回一个未解决的承诺。因此,您的 return 语句最终将解决该承诺并设置已解决的值。
    • 我会将网络工作者添加到真正的异步方法列表中:developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/…
    猜你喜欢
    • 1970-01-01
    • 2011-07-29
    • 2016-01-16
    • 2019-05-12
    • 2021-04-14
    • 1970-01-01
    • 2015-12-07
    • 2013-06-08
    • 1970-01-01
    相关资源
    最近更新 更多