fbpx
维基百科

async/await

计算机编程中,async/await模式是一种存在于许多编程语言中的语法特性。这种模式使得异步非阻塞函数逻辑可以用一种类似于同步函数的方式进行构造。在语义上,它与协程的概念相关,且通常也使用类似的技术实现。该模式大都是为了让程序能够在等待一个异步的长耗时操作完成的同时,也可以正常执行代码,它通常表现为Promises或者类似的形式。

这一特性出现在C# 5.0[1]:10C++20Python 3.5、 F#HackJuliaDartKotlin 1.1、 Rust 1.39、[2] Nim 0.9.4、[3] JavaScript ES2017Swift 5.5[4]Zig[5]中。对于Scala则出现在一些beta版本、实验版本的插件和特定的一些实现中。[6]

历史 编辑

F# 2.0(2007)中添加了含await点的异步逻辑[7]这对于后来添加到C#中的async/await机制有所启发。[8]

微软在2011年的Async CTP(Community Technology Preview,社区技术预览版)C#中首次添加了async/await,随后在2012年的C# 5中正式发布了这一机制。[9][1]:10

Haskell的主要开发者Simon Marlow英语Simon Marlow于2012年开发了async包。[10]

Python 3.5(2015)中支持了async/await,加入两个新的关键字asyncawait[11]

TypeScript 1.7(2015)中支持了async/await。[12]

JavaScript在2017年支持了async/await,作为ES2017标准的一部分。

Rust 1.39.0(2019)中支持了async/await,加入一个关键字async和一个惰性求值await[13][14]

C++20(2020)中支持了async/await,加入三个新的关键字co_returnco_awaitco_yield

Swift 5.5(2021)中支持了async/await,加入三个新的关键字asyncawaitactor,其中actor来自于同时期发布的对演员模型的一种具体的实现。这一模型中直接用到了async/await

一个C#的例子 编辑

下面的函数用于从一个URI上下载数据,然后返回这个数据的长度,其中就是用了async/await模式。

public async Task<int> FindPageSizeAsync(Uri uri)  {  var client = new HttpClient();  byte[] data = await client.GetByteArrayAsync(uri);  return data.Length; } 

为了便于理解,我们将上面的这一函数称为主函数。注意下文中的Promise是一个概念上统称,而不一定是具体存在的一种对象。

  1. 当函数被async关键字标记时,它会被编译器认为是异步的。这样的函数中可能会有若干个await表达式,它们可将结果绑定到Promise对象上。[15]:165-168主函数就是一个典型的异步函数。
  2. 主函数的返回类型 Task<T>(其中T为泛型)是C#对Promise概念的一种实现,在代码中Task<int>表明该Promise对应的实际结果是int类型的。
  3. 当主函数被执行时,首先一个新的HttpClient实例会被赋给client。
  4. 接下来await后紧跟的一句表达式执行了client上的异步方法GetByteArrayAsync(uri)[16]:189-190, 344[1]:882,它会返回一个Task<byte[]>。由于该方法是异步的,它并不会等到下载完成才返回,而是以某种非阻塞的方式(例如后台进程)开始下载,然后马上返回一个没有被resolve也没有被rejectTask<byte[]>到主函数中。在这里,resolve和reject可以理解为Task对象上的两个自带的方法,resolve(x)表示执行完成,reject(x)表示执行失败,二者都表示执行结束,因而都可用于最终的传值。
  5. 由于该表达式返回的Task<byte[]>前有await,接下来主函数会直接返回一个类似于之前Task对象的Task<int>到其调用者。显然,其调用者并不会被阻塞。
  6. GetByteArrayAsync(uri)下载结束后,它会使用其下载的数据resolve它所返回的那个Task,即上文中的Task<byte>Resolve会触发一个回调函数,使得主函数继续向下执行return data.Length
  7. 然后与GetByteArrayAsync(uri)的行为类似,主函数也会使用return语句返回的值来resolve它所返回的Task<int>,触发一个回调函数,使得其调用者能够开始使用这一具体值。

异步函数内部可以根据需要使用多个await语句,每一个语句都会以相同的方式进行处理(实际上只有第一个await语句会返回Promise,其余的await都用机制类似的内部回调函数实现)。对于返回的Promise对象,算法中亦可以对其直接进行处理(例如先保存起来),从而实现先执行其它任务(包括触发新的异步Task),等到需要相关结果的时候才使用await语句处理Promise对象,拿到结果。

除了直接await之外,也有一些可以批量处理Promise对象的函数,比如C#中的Task.WhenAll()函数[1]:174-175[16]:664-665,它会返回一个Task(无值Task,可以理解为Task<void>)。这个被返回的Task会在Task.WhenAll()方法参数中提供的所有的Promise被resolve以后resolve。还有一些Promise返回类型支持通常async/await模式不会用到的一些方法,例如对Promise设置多个结果的回调函数、监听长耗时Task的执行进程等。

在C#和许多其它语言中,async/await模式并不是运行时的核心组成部分,而实际上会在编译的时候使用Lambda表达式或者计算续体来实现。例如上面的C#代码很可能会被编译器先转换成下面的代码,然后才被转换成字节码

public Task<int> FindPageSizeAsync(Uri uri)  {  var client = new HttpClient();  Task<byte[]> dataTask = client.GetByteArrayAsync(uri);  Task<int> afterDataTask = dataTask.ContinueWith((originalTask) => {  return originalTask.Result.Length;  });  return afterDataTask; } 

也正因此,如果某个函数需要返回一个Promise对象,但是其本身并不需要进行任何的await求值,那么它并不需要在函数声明前面加上async来让自己成为一个异步函数,而是可以直接返回一个Promise对象。例如,使用C#的Task.FromResult()方法[16]:656来返回一个马上resolve的Task,或者直接如同地铁换乘一样将其它函数所提供的Task直接原样返回。 不过对于这项功能必须要注意的一项是,尽管在异步函数内部的逻辑长得很像同步的形式,这些逻辑实际上是非阻塞的,甚至可能是多线程的。所以在await语句等待Promise被resolve的时候,可能会发生许多侵入性的事件。比如下面的代码,如果没有await,那么将始终执行成功,但是如果使用了async/await模式,就可能会出现state.a发生改变(被其它逻辑)的情况。

var a = state.a; var client = new HttpClient(); // 与a无关的语句 var data = await client.GetByteArrayAsync(uri); // 与a无关的语句 Debug.Assert(a == state.a); // ★这个语句可能会出现错误,因为state.a可能在await的过程中被其它逻辑篡改。 return data.Length; 

在F#中的使用 编辑

F#中的异步逻辑的具体实现是计算表达式。具体使用时并不需要加上特殊的标识符,例如async。在代码逻辑中,使用一个感叹号(!)来开始异步操作。

从URL下载数据的异步函数逻辑如下:

let asyncSumPageSizes (uris: #seq<Uri>) : Async<int> = async {  use httpClient = new HttpClient()  let! pages =   uris  |> Seq.map(httpClient.GetStringAsync >> Async.AwaitTask)  |> Async.Parallel  return pages |> Seq.fold (fun accumulator current -> current.Length + accumulator) 0 } 

在C#中的使用 编辑

微软将C#中的async/await模式称作“以任务为基础的异步模式”(Task-based Asynchronous Pattern,TAP)。[17]异步函数的返回值通常包括voidTaskTask<T>[16]:35[18]:546-547[1]:22, 182 ValueTaskValueTask<T>[16]:651-652[1]:182-184代码中还可以利用异步函数构造器(async method builders)自行定义异步函数的返回值类型,不过这一场景高阶且少见。[19]返回void的异步函数通常应该是事件监听器,对于一般的函数则应该返回Task对象,因为它能够提供更加直观的异常处理[20]

要在函数中使用await,必须在函数声明前加上async关键字。当需要函数返回Task<T>类型的值时,函数声明前要加上async关键字,同时应当返回T或兼容的类型,而非Task<T>本身;随后编译器就会将返回的T类型包装为Task<T>泛型。不过,当非异步函数(没有使用async声明的函数)返回Task<T>时,其值也可以被await

下列函数代码将使用await从URL下载数据。该函数的逻辑用await实现了同时触发多个任务,无需等待其完成,这使得下一个任务无需在上一个任务完成之后才触发(这是同步的逻辑)。

public async Task<int> SumPageSizesAsync(IEnumerable<Uri> uris)  {  var client = new HttpClient();  int total = 0;  var loadUriTasks = new List<Task<byte[]>>();   foreach (var uri in uris)  {  var loadUriTask = client.GetByteArrayAsync(uri);  loadUriTasks.Add(loadUriTask );  }   foreach (var loadUriTask in loadUriTasks)  {  statusText.Text = $"已找到 {total} 个字节...";  var resourceAsBytes = await loadUriTask;  total += resourceAsBytes.Length;  }   statusText.Text = $"共找到 {total} 个字节。";   return total; } 

在Scala中的使用 编辑

Scala中有一个实验性的拓展Scala-async可以实现async/await模式。它提供了一个名为await的特殊函数。[6]与C#不同的是,Scala中的异步逻辑并不需要用async来标记。通过Scala-async,可以直接将异步逻辑用async函数调用的形式包围起来。

这是如何实现的 编辑

Scala-async所提供的async实际上是通过来实现的。编译器会调用不同的代码,然后产生一个有限状态机(通常认为这比单子实现更高效,但是更难以编写)。

在Python中的使用 编辑

在Python中的使用,在语法上与C#、JavaScript等类似。

import asyncio  async def main():  print("hello")  await asyncio.sleep(1)  print("world")  asyncio.run(main()) 

在JavaScript中的使用 编辑

JavaScript中的await运算符只能用于async标注的函数中,或者用于模块的最顶层。

如果await运算符后跟参数为Promise对象,那么函数逻辑会在该Promise对象被resolve之后继续执行,或者在它被reject以后抛出异常(可以进行异常处理);如果await运算符后跟参数不是Promise对象,那么该值会被直接返回(不会等待)。[21]

许多JavaScript库提供了可返回Promise对象的函数,它们都可以被await——只要符合JavaScript中的Promise规范。JQuery中函数返回的Promise在3.0版本以后才达到了Promises/A+兼容度。[22]

下面是一个使用例[23]

async function createNewDoc() {  let response = await db.post({}); // post a new doc  return db.get(response.id); // find by id }  async function main() {  try {  let doc = await createNewDoc();  console.log(doc);  } catch (err) {  console.log(err);  } } main(); 

Node.js 8 中包含的一样工具可将标准库中利用回调模式编写的函数当作Promise来使用。[24]

在C++中的使用 编辑

C++ 20中正式支持了await(在C++中是co_await)。GCCMSVC编译器支持async/await模式,包括协程以及相关的关键字例如co_awaitClang对此有部分支持。

值得注意的是std::promisestd::future虽然看起来像是可以被await求值的对象,但是实际上它们并没有实现任何类似于从协程中返回的值或者可被await的对象的相关属性;要使返回对象可以被await求值,必须在返回的对象类型上实现一系列的公共成员函数,例如await_readyawait_suspendawait_resume等。具体细节可以查看相关的参考。[25]

#include <iostream> #include "CustomAwaitableTask.h"  using namespace std;  CustomAwaitableTask<int> add(int a, int b) {  int c = a + b;  co_return c; }  CustomAwaitableTask<int> test() {  int ret = co_await add(1, 2);  cout << "return " << ret << endl;  co_return ret; }  int main() {  auto task = test();   return 0; } 

在C语言中的使用 编辑

C语言没有对await/async的官方支援。

某些协程库(例如s_task (页面存档备份,存于互联网档案馆))通过宏定义的方式, 实现了和其他语言类似的await/async的强制性语义要求,即:

1. 必须在async标注的函数内,才能调用await; 2. 等待一个标注为aysnc的函数,调用该函数时需要加上await; 
#include <stdio.h> #include "s_task.h" //定义协程任务需要的栈空间 int g_stack_main[64 * 1024 / sizeof(int)]; int g_stack0[64 * 1024 / sizeof(int)]; int g_stack1[64 * 1024 / sizeof(int)]; void sub_task(__async__, void* arg) {  int i;  int n = (int)(size_t)arg;  for (i = 0; i < 5; ++i) {  printf("task %d, delay seconds = %d, i = %d\n", n, n, i);  s_task_msleep(__await__, n * 1000); //等待一点时间  } } void main_task(__async__, void* arg) {  int i;  //创建两个子任务  s_task_create(g_stack0, sizeof(g_stack0), sub_task, (void*)1);  s_task_create(g_stack1, sizeof(g_stack1), sub_task, (void*)2);  for (i = 0; i < 4; ++i) {  printf("task_main arg = %p, i = %d\n", arg, i);  s_task_yield(__await__); //主动让出cpu  }  //等待子任务结束  s_task_join(__await__, g_stack0);  s_task_join(__await__, g_stack1); } int main(int argc, char* argv) {  s_task_init_system();  //创建一个任务  s_task_create(g_stack_main, sizeof(g_stack_main), main_task, (void*)(size_t)argc);  s_task_join(__await__, g_stack_main);  printf("all task is over\n");  return 0; } 

参考文献 编辑

  1. ^ 1.0 1.1 1.2 1.3 1.4 1.5 Skeet, Jon. C# in Depth. Manning. ISBN 978-1617294532. 
  2. ^ Announcing Rust 1.39.0. [2019-11-07] (英语). 
  3. ^ Version 0.9.4 released - Nim blog. [2020-01-19] (英语). 
  4. ^ Concurrency — The Swift Programming Language (Swift 5.5). docs.swift.org. [2021-09-28]. 
  5. ^ Zig Language Reference. 
  6. ^ 6.0 6.1 Scala Async. GitHub. [20 October 2013]. 
  7. ^ Syme, Don; Petricek, Tomas; Lomov, Dmitry. The F# Asynchronous Programming Model. Springer Link. Lecture Notes in Computer Science 6539. 2011: 175–189 [2021-04-29]. ISBN 978-3-642-18377-5. doi:10.1007/978-3-642-18378-2_15 (英语). 
  8. ^ The Early History of F#, HOPL IV. ACM Digital Library. [2021-04-29] (英语). 
  9. ^ Hejlsberg, Anders. Anders Hejlsberg: Introducing Async – Simplifying Asynchronous Programming. Channel 9 MSDN. Microsoft. [5 January 2021] (英语). 
  10. ^ async: Run IO operations asynchronously and wait for their results. Hackage. 
  11. ^ What's New In Python 3.5 — Python 3.9.1 documentation. docs.python.org. [5 January 2021]. 
  12. ^ Gaurav, Seth. Announcing TypeScript 1.7. TypeScript. Microsoft. 30 November 2015 [5 January 2021]. 
  13. ^ Matsakis, Niko. Async-await on stable Rust! | Rust Blog. blog.rust-lang.org. Rust Blog. [5 January 2021] (英语). 
  14. ^ Rust Gets Zero-Cost Async/Await Support in Rust 1.39. 
  15. ^ Skeet, Jon. C# in Depth. Manning. ISBN 978-1617294532. 
  16. ^ 16.0 16.1 16.2 16.3 16.4 Albahari, Joseph. C# 10 in a Nutshell. O'Reilly. ISBN 978-1-098-12195-2. 
  17. ^ Task-based asynchronous pattern. Microsoft. [28 September 2020]. 
  18. ^ Price, Mark J. C# 8.0 and .NET Core 3.0 – Modern Cross-Platform Development: Build Applications with C#, .NET Core, Entity Framework Core, ASP.NET Core, and ML.NET Using Visual Studio Code. Packt. ISBN 978-1-098-12195-2. 
  19. ^ Tepliakov, Sergey. Extending the async methods in C#. Developer Support. 2018-01-11 [2022-10-30] (美国英语). 
  20. ^ Stephen Cleary, Async/Await - Best Practices in Asynchronous Programming
  21. ^ await - JavaScript (MDN). [2 May 2017]. 
  22. ^ jQuery Core 3.0 Upgrade Guide. [2 May 2017]. 
  23. ^ Taming the asynchronous beast with ES7. [12 November 2015]. 
  24. ^ Foundation, Node.js. Node v8.0.0 (Current) - Node.js. Node.js. 
  25. ^ Coroutines (C++20). 

async, await, 本條目存在以下問題, 請協助改善本條目或在討論頁針對議題發表看法, 此條目需要补充更多来源, 2022年5月14日, 请协助補充多方面可靠来源以改善这篇条目, 无法查证的内容可能會因為异议提出而被移除, 致使用者, 请搜索一下条目的标题, 来源搜索, async, await, 网页, 新闻, 书籍, 学术, 图像, 以检查网络上是否存在该主题的更多可靠来源, 判定指引, 此條目可参照外語維基百科相應條目来扩充, 2018年4月9日, 若您熟悉来源语言和主题, 请协助参考外语维基百科扩充. 本條目存在以下問題 請協助改善本條目或在討論頁針對議題發表看法 此條目需要补充更多来源 2022年5月14日 请协助補充多方面可靠来源以改善这篇条目 无法查证的内容可能會因為异议提出而被移除 致使用者 请搜索一下条目的标题 来源搜索 Async await 网页 新闻 书籍 学术 图像 以检查网络上是否存在该主题的更多可靠来源 判定指引 此條目可参照外語維基百科相應條目来扩充 2018年4月9日 若您熟悉来源语言和主题 请协助参考外语维基百科扩充条目 请勿直接提交机械翻译 也不要翻译不可靠 低品质内容 依版权协议 译文需在编辑摘要注明来源 或于讨论页顶部标记 a href Template Translated page html title Template Translated page Translated page a 标签 在计算机编程中 async await模式是一种存在于许多编程语言中的语法特性 这种模式使得异步非阻塞函数逻辑可以用一种类似于同步函数的方式进行构造 在语义上 它与协程的概念相关 且通常也使用类似的技术实现 该模式大都是为了让程序能够在等待一个异步的长耗时操作完成的同时 也可以正常执行代码 它通常表现为Promises或者类似的形式 这一特性出现在C 5 0 1 10 C 20 Python 3 5 F Hack Julia Dart Kotlin 1 1 Rust 1 39 2 Nim 0 9 4 3 JavaScript ES2017 Swift 5 5 4 和Zig 5 中 对于Scala则出现在一些beta版本 实验版本的插件和特定的一些实现中 6 目录 1 历史 2 一个C 的例子 3 在F 中的使用 4 在C 中的使用 5 在Scala中的使用 5 1 这是如何实现的 6 在Python中的使用 7 在JavaScript中的使用 8 在C 中的使用 9 在C语言中的使用 10 参考文献历史 编辑F 2 0 2007 中添加了含await点的异步逻辑 7 这对于后来添加到C 中的async await机制有所启发 8 微软在2011年的Async CTP Community Technology Preview 社区技术预览版 C 中首次添加了async await 随后在2012年的C 5中正式发布了这一机制 9 1 10Haskell的主要开发者Simon Marlow 英语 Simon Marlow 于2012年开发了async包 10 Python 3 5 2015 中支持了async await 加入两个新的关键字async和await 11 TypeScript 1 7 2015 中支持了async await 12 JavaScript在2017年支持了async await 作为ES2017标准的一部分 Rust 1 39 0 2019 中支持了async await 加入一个关键字async和一个惰性求值await 13 14 C 20 2020 中支持了async await 加入三个新的关键字co return co await和co yield Swift 5 5 2021 中支持了async await 加入三个新的关键字async await和actor 其中actor来自于同时期发布的对演员模型的一种具体的实现 这一模型中直接用到了async await一个C 的例子 编辑下面的函数用于从一个URI上下载数据 然后返回这个数据的长度 其中就是用了async await模式 public async Task lt int gt FindPageSizeAsync Uri uri var client new HttpClient byte data await client GetByteArrayAsync uri return data Length 为了便于理解 我们将上面的这一函数称为主函数 注意下文中的Promise是一个概念上统称 而不一定是具体存在的一种对象 当函数被async关键字标记时 它会被编译器认为是异步的 这样的函数中可能会有若干个await表达式 它们可将结果绑定到Promise对象上 15 165 168主函数就是一个典型的异步函数 主函数的返回类型 Task lt T gt 其中T为泛型 是C 对Promise概念的一种实现 在代码中Task lt int gt 表明该Promise对应的实际结果是int类型的 当主函数被执行时 首先一个新的HttpClient实例会被赋给client 接下来await后紧跟的一句表达式执行了client上的异步方法GetByteArrayAsync uri 16 189 190 344 1 882 它会返回一个Task lt byte gt 由于该方法是异步的 它并不会等到下载完成才返回 而是以某种非阻塞的方式 例如后台进程 开始下载 然后马上返回一个没有被resolve也没有被reject的Task lt byte gt 到主函数中 在这里 resolve和reject可以理解为Task对象上的两个自带的方法 resolve x 表示执行完成 reject x 表示执行失败 二者都表示执行结束 因而都可用于最终的传值 由于该表达式返回的Task lt byte gt 前有await 接下来主函数会直接返回一个类似于之前Task对象的Task lt int gt 到其调用者 显然 其调用者并不会被阻塞 当GetByteArrayAsync uri 下载结束后 它会使用其下载的数据resolve它所返回的那个Task 即上文中的Task lt byte gt Resolve会触发一个回调函数 使得主函数继续向下执行return data Length 然后与GetByteArrayAsync uri 的行为类似 主函数也会使用return语句返回的值来resolve它所返回的Task lt int gt 触发一个回调函数 使得其调用者能够开始使用这一具体值 异步函数内部可以根据需要使用多个await语句 每一个语句都会以相同的方式进行处理 实际上只有第一个await语句会返回Promise 其余的await都用机制类似的内部回调函数实现 对于返回的Promise对象 算法中亦可以对其直接进行处理 例如先保存起来 从而实现先执行其它任务 包括触发新的异步Task 等到需要相关结果的时候才使用await语句处理Promise对象 拿到结果 除了直接await之外 也有一些可以批量处理Promise对象的函数 比如C 中的Task WhenAll 函数 1 174 175 16 664 665 它会返回一个Task 无值Task 可以理解为Task lt void gt 这个被返回的Task会在Task WhenAll 方法参数中提供的所有的Promise被resolve以后resolve 还有一些Promise返回类型支持通常async await模式不会用到的一些方法 例如对Promise设置多个结果的回调函数 监听长耗时Task的执行进程等 在C 和许多其它语言中 async await模式并不是运行时的核心组成部分 而实际上会在编译的时候使用Lambda表达式或者计算续体来实现 例如上面的C 代码很可能会被编译器先转换成下面的代码 然后才被转换成字节码 public Task lt int gt FindPageSizeAsync Uri uri var client new HttpClient Task lt byte gt dataTask client GetByteArrayAsync uri Task lt int gt afterDataTask dataTask ContinueWith originalTask gt return originalTask Result Length return afterDataTask 也正因此 如果某个函数需要返回一个Promise对象 但是其本身并不需要进行任何的await求值 那么它并不需要在函数声明前面加上async来让自己成为一个异步函数 而是可以直接返回一个Promise对象 例如 使用C 的Task FromResult 方法 16 656来返回一个马上resolve的Task 或者直接如同地铁换乘一样将其它函数所提供的Task直接原样返回 不过对于这项功能必须要注意的一项是 尽管在异步函数内部的逻辑长得很像同步的形式 这些逻辑实际上是非阻塞的 甚至可能是多线程的 所以在await语句等待Promise被resolve的时候 可能会发生许多侵入性的事件 比如下面的代码 如果没有await 那么将始终执行成功 但是如果使用了async await模式 就可能会出现state a发生改变 被其它逻辑 的情况 var a state a var client new HttpClient 与a无关的语句 var data await client GetByteArrayAsync uri 与a无关的语句 Debug Assert a state a 这个语句可能会出现错误 因为state a可能在await的过程中被其它逻辑篡改 return data Length 在F 中的使用 编辑F 中的异步逻辑的具体实现是计算表达式 具体使用时并不需要加上特殊的标识符 例如async 在代码逻辑中 使用一个感叹号 来开始异步操作 从URL下载数据的异步函数逻辑如下 let asyncSumPageSizes uris seq lt Uri gt Async lt int gt async use httpClient new HttpClient let pages uris gt Seq map httpClient GetStringAsync gt gt Async AwaitTask gt Async Parallel return pages gt Seq fold fun accumulator current gt current Length accumulator 0 在C 中的使用 编辑微软将C 中的async await模式称作 以任务为基础的异步模式 Task based Asynchronous Pattern TAP 17 异步函数的返回值通常包括void Task Task lt T gt 16 35 18 546 547 1 22 182 ValueTask和ValueTask lt T gt 16 651 652 1 182 184代码中还可以利用异步函数构造器 async method builders 自行定义异步函数的返回值类型 不过这一场景高阶且少见 19 返回void的异步函数通常应该是事件监听器 对于一般的函数则应该返回Task对象 因为它能够提供更加直观的异常处理 20 要在函数中使用await 必须在函数声明前加上async关键字 当需要函数返回Task lt T gt 类型的值时 函数声明前要加上async关键字 同时应当返回T或兼容的类型 而非Task lt T gt 本身 随后编译器就会将返回的T类型包装为Task lt T gt 泛型 不过 当非异步函数 没有使用async声明的函数 返回Task lt T gt 时 其值也可以被await 下列函数代码将使用await从URL下载数据 该函数的逻辑用await实现了同时触发多个任务 无需等待其完成 这使得下一个任务无需在上一个任务完成之后才触发 这是同步的逻辑 public async Task lt int gt SumPageSizesAsync IEnumerable lt Uri gt uris var client new HttpClient int total 0 var loadUriTasks new List lt Task lt byte gt gt foreach var uri in uris var loadUriTask client GetByteArrayAsync uri loadUriTasks Add loadUriTask foreach var loadUriTask in loadUriTasks statusText Text 已找到 total 个字节 var resourceAsBytes await loadUriTask total resourceAsBytes Length statusText Text 共找到 total 个字节 return total 在Scala中的使用 编辑Scala中有一个实验性的拓展Scala async可以实现async await模式 它提供了一个名为await的特殊函数 6 与C 不同的是 Scala中的异步逻辑并不需要用async来标记 通过Scala async 可以直接将异步逻辑用async函数调用的形式包围起来 这是如何实现的 编辑 Scala async所提供的async实际上是通过宏来实现的 编译器会调用不同的代码 然后产生一个有限状态机 通常认为这比单子实现更高效 但是更难以编写 在Python中的使用 编辑在Python中的使用 在语法上与C JavaScript等类似 import asyncio async def main print hello await asyncio sleep 1 print world asyncio run main 在JavaScript中的使用 编辑JavaScript中的await运算符只能用于async标注的函数中 或者用于模块的最顶层 如果await运算符后跟参数为Promise对象 那么函数逻辑会在该Promise对象被resolve之后继续执行 或者在它被reject以后抛出异常 可以进行异常处理 如果await运算符后跟参数不是Promise对象 那么该值会被直接返回 不会等待 21 许多JavaScript库提供了可返回Promise对象的函数 它们都可以被await 只要符合JavaScript中的Promise规范 JQuery中函数返回的Promise在3 0版本以后才达到了Promises A 兼容度 22 下面是一个使用例 23 async function createNewDoc let response await db post post a new doc return db get response id find by id async function main try let doc await createNewDoc console log doc catch err console log err main Node js 8 中包含的一样工具可将标准库中利用回调模式编写的函数当作Promise来使用 24 在C 中的使用 编辑C 20中正式支持了await 在C 中是co await GCC MSVC编译器支持async await模式 包括协程以及相关的关键字例如co await Clang对此有部分支持 值得注意的是std promise和std future虽然看起来像是可以被await求值的对象 但是实际上它们并没有实现任何类似于从协程中返回的值或者可被await的对象的相关属性 要使返回对象可以被await求值 必须在返回的对象类型上实现一系列的公共成员函数 例如await ready await suspend和await resume等 具体细节可以查看相关的参考 25 include lt iostream gt include CustomAwaitableTask h using namespace std CustomAwaitableTask lt int gt add int a int b int c a b co return c CustomAwaitableTask lt int gt test int ret co await add 1 2 cout lt lt return lt lt ret lt lt endl co return ret int main auto task test return 0 在C语言中的使用 编辑C语言没有对await async的官方支援 某些协程库 例如s task 页面存档备份 存于互联网档案馆 通过宏定义的方式 实现了和其他语言类似的await async的强制性语义要求 即 1 必须在async标注的函数内 才能调用await 2 等待一个标注为aysnc的函数 调用该函数时需要加上await include lt stdio h gt include s task h 定义协程任务需要的栈空间 int g stack main 64 1024 sizeof int int g stack0 64 1024 sizeof int int g stack1 64 1024 sizeof int void sub task async void arg int i int n int size t arg for i 0 i lt 5 i printf task d delay seconds d i d n n n i s task msleep await n 1000 等待一点时间 void main task async void arg int i 创建两个子任务 s task create g stack0 sizeof g stack0 sub task void 1 s task create g stack1 sizeof g stack1 sub task void 2 for i 0 i lt 4 i printf task main arg p i d n arg i s task yield await 主动让出cpu 等待子任务结束 s task join await g stack0 s task join await g stack1 int main int argc char argv s task init system 创建一个任务 s task create g stack main sizeof g stack main main task void size t argc s task join await g stack main printf all task is over n return 0 参考文献 编辑 1 0 1 1 1 2 1 3 1 4 1 5 Skeet Jon C in Depth Manning ISBN 978 1617294532 Announcing Rust 1 39 0 2019 11 07 英语 Version 0 9 4 released Nim blog 2020 01 19 英语 Concurrency The Swift Programming Language Swift 5 5 docs swift org 2021 09 28 Zig Language Reference 6 0 6 1 Scala Async GitHub 20 October 2013 Syme Don Petricek Tomas Lomov Dmitry The F Asynchronous Programming Model Springer Link Lecture Notes in Computer Science 6539 2011 175 189 2021 04 29 ISBN 978 3 642 18377 5 doi 10 1007 978 3 642 18378 2 15 英语 The Early History of F HOPL IV ACM Digital Library 2021 04 29 英语 Hejlsberg Anders Anders Hejlsberg Introducing Async Simplifying Asynchronous Programming Channel 9 MSDN Microsoft 5 January 2021 英语 async Run IO operations asynchronously and wait for their results Hackage What s New In Python 3 5 Python 3 9 1 documentation docs python org 5 January 2021 Gaurav Seth Announcing TypeScript 1 7 TypeScript Microsoft 30 November 2015 5 January 2021 Matsakis Niko Async await on stable Rust Rust Blog blog rust lang org Rust Blog 5 January 2021 英语 Rust Gets Zero Cost Async Await Support in Rust 1 39 Skeet Jon C in Depth Manning ISBN 978 1617294532 16 0 16 1 16 2 16 3 16 4 Albahari Joseph C 10 in a Nutshell O Reilly ISBN 978 1 098 12195 2 Task based asynchronous pattern Microsoft 28 September 2020 Price Mark J C 8 0 and NET Core 3 0 Modern Cross Platform Development Build Applications with C NET Core Entity Framework Core ASP NET Core and ML NET Using Visual Studio Code Packt ISBN 978 1 098 12195 2 Tepliakov Sergey Extending the async methods in C Developer Support 2018 01 11 2022 10 30 美国英语 Stephen Cleary Async Await Best Practices in Asynchronous Programming await JavaScript MDN 2 May 2017 jQuery Core 3 0 Upgrade Guide 2 May 2017 Taming the asynchronous beast with ES7 12 November 2015 Foundation Node js Node v8 0 0 Current Node js Node js Coroutines C 20 取自 https zh wikipedia org w index php title Async await amp oldid 77844175, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。