await(C# 参考)await (C# Reference)

await 运算符应用于异步方法中的任务,在方法的执行中插入挂起点,直到所等待的任务完成。The await operator is applied to a task in an asynchronous method to insert a suspension point in the execution of the method until the awaited task completes. 任务表示正在进行的工作。The task represents ongoing work.

await 仅可用于由 async 关键字修改的异步方法中。await can only be used in an asynchronous method modified by the async keyword. 使用 async 修饰符定义并且通常包含一个或多个 await 表达式的这类方法称为异步方法。Such a method, defined by using the async modifier and usually containing one or more await expressions, is referred to as an async method.

备注

asyncawait 关键字是在 C# 5 中引入的。The async and await keywords were introduced in C# 5. 有关异步编程的说明,请参阅使用 Async 和 Await 的异步编程For an introduction to async programming, see Asynchronous Programming with async and await.

应用 await 运算符的任务通常由实现基于任务的异步模式的方法调用返回。The task to which the await operator is applied typically is returned by a call to a method that implements the Task-Based Asynchronous Pattern. 包括返回 TaskTask<TResult>System.Threading.Tasks.ValueType<TResult> 对象的方法。They include methods that return Task, Task<TResult>, and System.Threading.Tasks.ValueType<TResult> objects.

如下示例中,HttpClient.GetByteArrayAsync 方法返回 Task<byte[]>In the following example, the HttpClient.GetByteArrayAsync method returns a Task<byte[]>. 当任务完成时,任务约定生成实际字节数组。The task is a promise to produce the actual byte array when the task is complete. await 运算符挂起执行,直到 GetByteArrayAsync 方法完成其操作。The await operator suspends execution until the work of the GetByteArrayAsync method is complete. 同时,控制权会返回给 GetPageSizeAsync 的调用方。In the meantime, control is returned to the caller of GetPageSizeAsync. 任务结束执行时,await 表达式的计算结果为字节数组。When the task finishes execution, the await expression evaluates to a byte array.

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length > 1)
         GetPageSizeAsync(args[1]).Wait();
      else
         Console.WriteLine("Enter at least one URL on the command line.");
   }

   private static async Task GetPageSizeAsync(string url)  
   {  
       var client = new HttpClient();  
       var uri = new Uri(Uri.EscapeUriString(url));
       byte[] urlContents = await client.GetByteArrayAsync(uri);
       Console.WriteLine($"{url}: {urlContents.Length/2:N0} characters");  
   }  
}
// The following call from the command line:
//    await1 http://docs.microsoft.com
// displays output like the following: 
//   http://docs.microsoft.com: 7,967 characters


重要

有关完整示例,请参阅演练:使用 async 和 await 访问 WebFor the complete example, see Walkthrough: Accessing the Web by Using Async and Await. 可以从 Microsoft 网站的开发者代码示例中下载示例。You can download the sample from Developer Code Samples on the Microsoft website. 该示例处于 AsyncWalkthrough_HttpClient 项目中。The example is in the AsyncWalkthrough_HttpClient project.

如前一示例所示,如果 await 应用于返回 Task<TResult> 的方法调用的结果,则 await 表达式的类型为 TResultAs shown in the previous example, if await is applied to the result of a method call that returns a Task<TResult>, then the type of the await expression is TResult. 如果 await 应用于返回 Task 的方法调用的结果,则 await 表达式的类型为 voidIf await is applied to the result of a method call that returns a Task, then the type of the await expression is void. 以下示例演示了差异。The following example illustrates the difference.

// await keyword used with a method that returns a Task<TResult>.  
TResult result = await AsyncMethodThatReturnsTaskTResult();  
  
// await keyword used with a method that returns a Task.  
await AsyncMethodThatReturnsTask();  

// await keyword used with a method that returns a ValueTask<TResult>.
TResult result = await AsyncMethodThatReturnsValueTaskTResult();

await 表达式不阻止正在执行它的线程。An await expression does not block the thread on which it is executing. 而是使编译器将剩下的异步方法注册为等待任务的延续任务。Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. 控制权随后会返回给异步方法的调用方。Control then returns to the caller of the async method. 任务完成时,它会调用其延续任务,异步方法的执行会在暂停的位置处恢复。When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.

await 表达式只能在由 async 修饰符标记的封闭方法体、lambda 表达式或异步方法中出现。An await expression can occur only in the body of its enclosing method, lambda expression, or anonymous method, which must be marked with an async modifier. 术语 await 在该上下文中仅用作关键字。The term await serves as a keyword only in that context. 在其他位置,它会解释为标识符。Elsewhere, it is interpreted as an identifier. 在方法、Lambda 表达式或匿名方法中,await 表达式不能在同步函数体、查询表达式、lock 语句块或不安全上下文中出现。Within the method, lambda expression, or anonymous method, an await expression cannot occur in the body of a synchronous function, in a query expression, in the block of a lock statement, or in an unsafe context.

异常Exceptions

大多数异步方法返回 TaskTask<TResult>Most async methods return a Task or Task<TResult>. 返回任务的属性携带有关其状态和历史记录的信息,如任务是否完成、异步方法是否导致异常或已取消以及最终结果是什么。The properties of the returned task carry information about its status and history, such as whether the task is complete, whether the async method caused an exception or was canceled, and what the final result is. await 运算符通过在由 GetAwaiter 方法返回的对象上调用方法来访问那些属性。The await operator accesses those properties by calling methods on the object returned by the GetAwaiter method.

如果等待的返回任务的异步方法会导致异常,则 await 运算符重新引发异常。If you await a task-returning async method that causes an exception, the await operator rethrows the exception.

如果等待的任务返回异步方法取消,则 await 运算符会重新引发 OperationCanceledExceptionIf you await a task-returning async method that's canceled, the await operator rethrows an OperationCanceledException.

处于故障状态的单个任务可以反映多个异常。A single task that is in a faulted state can reflect multiple exceptions. 例如,任务可能是对 Task.WhenAll 调用的结果。For example, the task might be the result of a call to Task.WhenAll. 等待此类任务时,等待操作仅重新引发异常之一。When you await such a task, the await operation rethrows only one of the exceptions. 但是,无法预测重新引发的异常。However, you can't predict which of the exceptions is rethrown.

有关异步方法中的错误处理的示例,请参阅 try catchFor examples of error handling in async methods, see try-catch.

示例Example

下面的示例返回页面(页面 URL 作为命令行参数传递给页面)中的字符总数。The following example returns the total number of characters in the pages whose URLs are passed to it as command line arguments. 此示例调用 GetPageLengthsAsync 方法,此方法标记有 async 关键字。The example calls the GetPageLengthsAsync method, which is marked with the async keyword. GetPageLengthsAsync 方法使用 await 关键字等待对 HttpClient.GetStringAsync 方法的调用。The GetPageLengthsAsync method in turn uses the await keyword to await calls to the HttpClient.GetStringAsync method.

using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   static async Task Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length < 2)
         throw new ArgumentNullException("No URIs specified on the command line.");

      // Don't pass the executable file name
      var uris = args.Skip(1).ToArray();

      long characters = await GetPageLengthsAsync(uris);
      Console.WriteLine($"{uris.Length} pages, {characters:N0} characters");
   }

   private static async Task<long> GetPageLengthsAsync(string[] uris)
   {
      var client = new HttpClient();
      long pageLengths = 0;

      foreach (var uri in uris)
      {
         var escapedUri = new Uri(Uri.EscapeUriString(uri));
         string pageContents = await client.GetStringAsync(escapedUri);
         Interlocked.Add(ref pageLengths, pageContents.Length);
      }
      
      return pageLengths;
   }
}

前面的示例使用 C# 7.1,它支持 async Main 方法The preceding example uses C# 7.1, which supports the async Main method. 因为早期的 C# 版本不支持返回 TaskTask<TResult> 的应用程序入口点,无法将 async 修饰符应用于 Main 方法并等待 GetPageLengthsAsync 方法调用。Because earlier C# versions don't support application entry points that return Task or Task<TResult>, you cannot apply the async modifier to the Main method and await the GetPageLengthsAsync method call. 在这种情况下,可通过检索 Task<TResult>.Result 属性的值来确保 Main 方法等待异步操作完成。In that case, you can ensure that the Main method waits for the async operation to complete by retrieving the value of the Task<TResult>.Result property. 对于不返回值的任务,可调用 Task.Wait 方法。For tasks that do not return a value, you can call the Task.Wait method. 有关如何选择语言版本的信息,请参阅选择 C# 语言版本For information about how to select the language version, see Select the C# language version.

请参阅See also