await 运算符 - 异步等待任务完成

await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。 异步操作完成后,await 运算符将返回操作的结果(如果有)。 当 await 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。 await 运算符不会阻止计算异步方法的线程。 当 await 运算符暂停其所属的异步方法时,控件将返回到方法的调用方。

在下面的示例中,HttpClient.GetByteArrayAsync 方法返回 Task<byte[]> 实例,该实例表示在完成时生成字节数组的异步操作。 在操作完成之前,await 运算符将暂停 DownloadDocsMainPageAsync 方法。 当 DownloadDocsMainPageAsync 暂停时,控件将返回到 Main 方法,该方法是 DownloadDocsMainPageAsync 的调用方。 Main 方法将执行,直至它需要 DownloadDocsMainPageAsync 方法执行的异步操作的结果。 当 GetByteArrayAsync 获取所有字节时,将计算 DownloadDocsMainPageAsync 方法的其余部分。 之后,将计算 Main 方法的其余部分。

public class AwaitOperator
{
    public static async Task Main()
    {
        Task<int> downloading = DownloadDocsMainPageAsync();
        Console.WriteLine($"{nameof(Main)}: Launched downloading.");

        int bytesLoaded = await downloading;
        Console.WriteLine($"{nameof(Main)}: Downloaded {bytesLoaded} bytes.");
    }

    private static async Task<int> DownloadDocsMainPageAsync()
    {
        Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: About to start downloading.");

        var client = new HttpClient();
        byte[] content = await client.GetByteArrayAsync("https://learn.microsoft.com/en-us/");

        Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: Finished downloading.");
        return content.Length;
    }
}
// Output similar to:
// DownloadDocsMainPageAsync: About to start downloading.
// Main: Launched downloading.
// DownloadDocsMainPageAsync: Finished downloading.
// Main: Downloaded 27700 bytes.

必须提供 await 表达式的操作数,才能在任务完成时进行通知。 通常,任务完成时(无论成功还是失败)都会调用委托。 C# 语言规范的 await 部分提供了有关如何实现这些通知的详细信息。

前一个示例使用异步 Main 方法。 有关详细信息,请参阅 Main 方法中的 await 运算符部分。

注意

有关异步编程的介绍,请参阅使用 async 和 await 的异步编程。 利用 asyncawait 的异步编程遵循基于任务的异步模式

只能在通过 async 关键字修改的方法、lambda 表达式匿名方法中使用 await 运算符。 在异步方法中,不能在同步函数的本地主体、lock 语句块内以及不安全的上下文中使用 await 运算符。

await 运算符的操作数通常是以下其中一个 .NET 类型:TaskTask<TResult>ValueTaskValueTask<TResult>。 但是,任何可等待表达式都可以是 await 运算符的操作数。 有关详细信息,请参阅 C# 语言规范中的可等待表达式部分。

如果表达式 t 的类型为 Task<TResult>ValueTask<TResult>,则表达式 await t 的类型为 TResult。 如果 t 的类型为 TaskValueTask,则 await t 的类型为 void。 在这两种情况下,如果 t 引发异常,则 await t 将重新引发异常。

异步流和可释放对象

可使用 await foreach 语句来使用异步数据流。 有关详细信息,请参阅迭代语句一文的 foreach 语句部分。

可使用 await using 语句来处理异步可释放对象,即其类型可实现 IAsyncDisposable 接口的对象。 有关详细信息,请参阅实现 DisposeAsync 方法一文中的使用异步可释放对象部分。

Main 方法中的 await 运算符

作为应用程序入口点的 Main 方法可以返回 TaskTask<int>,使其成为异步的,以便在其主体中使用 await 运算符。 在较早的 C# 版本中,为了确保 Main 方法等待异步操作完成,可以检索由相应的异步方法返回的 Task<TResult> 实例的 Task<TResult>.Result 属性值。 对于不生成值的异步操作,可以调用 Task.Wait 方法。 有关如何选择语言版本的信息,请参阅 C# 语言版本管理

C# 语言规范

有关详细信息,请参阅 C# 语言规范中的 Await 表达式部分。

另请参阅