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.

Примечание

Ключевые слова async и await появились в версии 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. К ним относятся методы, возвращающие объекты Task, Task<TResult>, ValueTask и ValueTask<TResult>.They include methods that return Task, Task<TResult>, ValueTask, and ValueTask<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.For the complete example, see Walkthrough: Accessing the Web by Using Async and Await. Вы можете скачать этот пример в разделе Примеры кода для разработчиков на веб-сайте Майкрософт.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 является TResult.As 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 является void.If 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.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. Внутри метода, лямбда-выражения или анонимного метода выражение 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

Большинство асинхронных методов возвращает Task или Task<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 повторно создает исключение OperationCanceledException.If 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. Если вы ожидаете такую задачу, операция await повторно вызывает только одно из исключений.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-catch.For 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# не поддерживают точки входа приложения, которые возвращают Task или Task<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