【问题标题】:Why 'await' requires 'async' in function definition为什么'await'在函数定义中需要'async'
【发布时间】:2017-07-03 22:46:54
【问题描述】:

我目前正在学习 Dart,但这也适用于 JavaScript 世界中正在发生的事情,而且 C# 似乎也使用了相同的模式。

在 Dart 中,任何使用 await 的函数本身必须通过 async 标记为异步,如下所示:

import "dart:html";

main() async {
  var context = querySelector("canvas").context2D;
  var running = true;

  while (running) {
    var time = await window.animationFrame;
    ...
  }
}

这对我来说没有意义。如果一个函数正在等待异步函数完成,那么它是否不被视为阻塞?为什么 JS 和 Dart 都要求标为异步?会不会正好相反?

对我来说,如果调用函数必须使用 async 关键字,如果它调用任何在其定义中也包含它的函数,那将更有意义。在此模式中,await 将用于将异步函数转换为同步函数。

这种方式也可以避免重复的函数,因为现在库似乎总是有func()funcSync()funcAsync()

【问题讨论】:

  • AFAIK 异步和阻塞之间的区别在于,您可以在单个线程中拥有多个异步进程(其中一个可以工作,而其他进程不工作),而阻塞进程会阻塞整个线程。跨度>
  • 这个问题的有趣之处在于,从字面上看,实际的答案(在 C# 中)只是 _"async 关键字是必要的,因为 await 是在后来的开发中引入的语言,并且代码中可能包含用户定义的标识符awaitasync 允许代码选择加入,并防止破坏不支持异步的现有代码。否则,编译器将刚刚自动将包含await 的方法变成了异步方法,包含yield 的方法是自动考虑的迭代器方法。”
  • 但这似乎并不是你真正要问的。你真的只是不明白async/await 是如何工作的,也不了解语义是什么。您应该阅读有关该主题的众多现有问答之一。我们真的不需要在 Stack Overflow 上再次对其进行重新散列。
  • 您希望您的问题以“过于宽泛”或“主要基于意见”的方式结束?事实上,标记的副本解决了您在涉及 C# 的范围内提出的问题。如果您不想要以 C# 为中心的答案,请不要将 C# 拖入问题中。 Stack Overflow 不是研究比较语言设计的地方,每种语言中的异步功能都不够相似,无法讨论,更不用说回答你的问题,而不会过于宽泛或只是一个见仁见智的问题。
  • 这绝对不是重复的,也不是太宽泛或主要基于意见。 This 我会怎么回答这个问题。

标签: javascript asynchronous promise dart


【解决方案1】:

async/await 的基本语义在 F#、VB、C#、Python、Dart、Hack 和 JavaScript 中是相同的。所以我认为这个问题有足够的其他语言的答案。不过既然已经重新开放了……

如果一个函数正在等待异步函数完成,那么它是否不被视为阻塞?

没有。这样想:

  • “异步”表示“不阻塞调用线程”。
  • “同步”表示“阻塞调用线程”。

在异步方法/函数中,方法/函数可以在await暂停,但在暂停时它不会阻塞调用线程。该函数串行运行(一次一条语句),但异步(不阻塞调用线程)。

对我来说,如果调用函数必须使用 async 关键字,如果它调用任何在其定义中也包含它的函数,那么它会更有意义。

这就是它已经工作的方式...await 使用 promise/future/task-returning 方法/函数,async 将方法/函数标记为能够使用 await

这样也可以避免重复功能

这对于历史上阻塞的命令式语言是不可能的。方法/函数要么阻塞调用线程直到它完成,要么不阻塞。它可以是同步的,也可以是异步的。

不过,对于异步方法/函数,还有一些有趣的替代方法:

Go 在历史上没有阻塞;您可以将其视为一种语言,其中每个方法/函数都可能是异步的;在任何其他运行时执行此操作将是灾难性的,但 Go 通过实现具有消息传递的严格 goroutine 系统来避免这些问题 - 不允许共享内存或线程。

另一个例子是纯函数式语言,其中 Future 只是另一个 monad,不需要对 async/await 关键字的特殊编译器支持。

【讨论】:

  • 如果您在 for 循环中执行一百万个项目数组冒泡排序,您也会阻塞,基本上您在 javascript 中执行的任何操作都会阻塞,除非使用 Promise 或超时。您执行的任何不使用await 的阻塞代码都不需要是异步的。这应该是开发商的决定。您甚至可以实现自己的 await,方法是执行一个在 promise 完成时中断的循环,而不需要封闭函数是异步的。
【解决方案2】:

在 Dart 中async 表示包含await 的代码需要重写。 async/await 只是 Future.then() 基本 API 的语法糖,代码在编译和执行之前被重写为这种基于 Future 的规范形式。

因此await 不会造成代码阻塞。

还有其他受支持的标记,例如用于生成器的 *sync*async

另请参阅 - Dart async/await internals - https://www.dartlang.org/articles/language/await-async

【讨论】:

    【解决方案3】:

    我不确定您的问题与 C# 有什么关系,但 await 是 C# 中最不幸的关键字选择。如果您将await 读作resume after 或什至yield then resume after,则更容易理解await。当执行遇到await 时,它会挂起当前函数,返回给它的调用者。一旦等待的 Task 完成,当调用者需要结果时(来自它自己的 await 表达式),在 await 之后继续执行。

    【讨论】:

    • 这并不一定意味着它们具有相同的含义,尽管在这种情况下它们似乎确实如此。
    【解决方案4】:

    这是对“幕后”发生的事情的根本误解,我假设 javascript async/await 实现(至少在概念上)反映了 .NET 世界(起源于 F#)的实现。

    基本上:

    1. 各种“工作单元”“令牌”(在 C# 中为 JS 中的任务为 Promise)表示任务的完成,并可选择返回值。
    2. “异步”方法标有 async,以向编译器/解释器/运行时指示此方法将返回所述令牌,并且可能还会消耗一些令牌。
    3. 运行这一切的 thingamajig 将(在 C# 的情况下)偷偷地重写代码以基本上执行以下操作:

    1. 启动异步方法
    2. 做标准正常代码
    3. 遇到“令牌”提供方法
    4. 调用它,获取令牌
    5. 设置代码稍后可以跳回的位置
    6. 令牌是否完整?如果是,请继续,如果不是,请设置一个 回调,让运行时返回此位置

    在这个扩展的上下文中,关键字是有意义的:

    1. async(hronous) - 一种执行某些任务的方法,该任务可能需要不确定的时间量,可以等待。
    2. await - 表明你不是仅仅从异步方法中获取令牌并且什么都不做,而是你实际上想要设置所有的仪式来监听工作何时完成,然后继续之后。

    tl;dr

    只需将 async 和 await 视为不可见的回调,async 表示何时可以监听某些内容,而 await 是实际监听的内容。

    【讨论】:

      猜你喜欢
      • 2017-10-26
      • 1970-01-01
      • 2018-05-24
      • 2019-02-26
      • 2019-04-13
      • 2015-06-30
      • 2020-02-11
      • 1970-01-01
      相关资源
      最近更新 更多