编译器警告(等级 1)CS4014Compiler Warning (level 1) CS4014

在调用完成之前,会继续执行当前方法,原因是此调用不处于等待状态。Because this call is not awaited, execution of the current method continues before the call is completed. 请考虑向调用结果应用“await”运算符。Consider applying the 'await' operator to the result of the call.

当前方法调用返回 TaskTask<TResult> 的异步方法,而不对结果应用 await 运算符。The current method calls an async method that returns a Task or a Task<TResult> and doesn’t apply the await operator to the result. 对异步方法的调用会启动异步任务。The call to the async method starts an asynchronous task. 但是,由于没有应用 await 运算符,程序将继续运行,而不必等待任务完成。However, because no await operator is applied, the program continues without waiting for the task to complete. 在大多数情况下,此行为并不是您所期望的。In most cases, that behavior isn't what you expect. 方法调用的其他方面通常取决于调用的结果,而调用的方法至少应在从包含调用的方法返回之前完成。Usually other aspects of the calling method depend on the results of the call or, minimally, the called method is expected to complete before you return from the method that contains the call.

对于被调用的异步方法中引发的异常,如何进行处理同样是一个重要的问题。An equally important issue is what happens to exceptions that are raised in the called async method. 在返回 TaskTask<TResult> 的方法中引发的异常将存储在返回的任务中。An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. 如果不等待任务或不显式检查异常,该异常会丢失。If you don't await the task or explicitly check for exceptions, the exception is lost. 如果等待任务,将重新引发异常。If you await the task, its exception is rethrown.

作为最佳做法,应始终等待调用。As a best practice, you should always await the call.

只有当您确定不需要等待异步调用完成并且调用的方法不会引发任何异常时,才应考虑禁止显示警告。You should consider suppressing the warning only if you're sure that you don't want to wait for the asynchronous call to complete and that the called method won't raise any exceptions. 在此情况下,可以通过将调用的任务结果分配给变量来禁止显示警告。In that case, you can suppress the warning by assigning the task result of the call to a variable.

下面的示例演示如何生成警告,如何禁止显示警告以及如何等待调用。The following example shows how to cause the warning, how to suppress it, and how to await the call.

async Task CallingMethodAsync()  
{  
    resultsTextBox.Text += "\r\n  Entering calling method.";  
    // Variable delay is used to slow down the called method so that you can  
    // distinguish between awaiting and not awaiting in the program's output.  
    // You can adjust the value to produce the output that this topic shows  
    // after the code.  
    var delay = 5000;  
  
    // Call #1.  
    // Call an async method. Because you don't await it, its completion
    // isn't coordinated with the current method, CallingMethodAsync.  
    // The following line causes warning CS4014.  
    CalledMethodAsync(delay);  
  
    // Call #2.  
    // To suppress the warning without awaiting, you can assign the
    // returned task to a variable. The assignment doesn't change how  
    // the program runs. However, recommended practice is always to  
    // await a call to an async method.  
  
    // Replace Call #1 with the following line.  
    //Task delayTask = CalledMethodAsync(delay);  
  
    // Call #3  
    // To contrast with an awaited call, replace the unawaited call
    // (Call #1 or Call #2) with the following awaited call. Best
    // practice is to await the call.  
  
    //await CalledMethodAsync(delay);  
  
    // If the call to CalledMethodAsync isn't awaited, CallingMethodAsync  
    // continues to run and, in this example, finishes its work and returns  
    // to its caller.  
    resultsTextBox.Text += "\r\n  Returning from calling method.";  
}  
  
async Task CalledMethodAsync(int howLong)  
{  
    resultsTextBox.Text +=
        "\r\n    Entering called method, starting and awaiting Task.Delay.";  
  
    // Slow the process down a little so that you can distinguish between  
    // awaiting and not awaiting in the program's output. Adjust the value  
    // for howLong if necessary.  
    await Task.Delay(howLong);  
    resultsTextBox.Text +=
        "\r\n    Task.Delay is finished--returning from called method.";  
}  

在此示例中,如果选择调用 1 或调用 2,未等待的异步方法 (CalledMethodAsync) 将在调用方 (CallingMethodAsync) 和调用方的调用方 (startButton_Click) 完成后完成。In the example, if you choose Call #1 or Call #2, the unawaited async method (CalledMethodAsync) finishes after both its caller (CallingMethodAsync) and the caller's caller (startButton_Click) are complete. 以下输出的最后一行演示了调用的方法何时完成。The last line in the following output shows you when the called method finishes. 输出内容标示了整个示例中何时进入和退出调用 CallingMethodAsync 的事件处理程序。Entry to and exit from the event handler that calls CallingMethodAsync in the full example are marked in the output.

Entering the Click event handler.  
  Entering calling method.  
    Entering called method, starting and awaiting Task.Delay.  
  Returning from calling method.  
Exiting the Click event handler.  
    Task.Delay is finished--returning from called method.  

通过使用 #pragma warning 指令,也可以禁止显示编译器警告。You can also suppress compiler warnings by using #pragma warning directives.

示例Example

以下 Windows Presentation Foundation (WPF) 应用程序包含上一示例中的方法。The following Windows Presentation Foundation (WPF) application contains the methods from the previous example. 下列步骤用于设置该应用程序。The following steps set up the application.

  1. 创建一个 WPF 应用程序,将其命名为 AsyncWarningCreate a WPF application, and name it AsyncWarning.

  2. 在 Visual Studio 代码编辑器中,选择 “MainWindow.xaml” 选项卡。In the Visual Studio Code Editor, choose the MainWindow.xaml tab.

    如果此选项卡不可见,则在解决方案资源管理器 中,打开 MainWindow.xaml 的快捷菜单,然后选择“查看代码” 。If the tab isn't visible, open the shortcut menu for MainWindow.xaml in Solution Explorer, and then choose View Code.

  3. 在“XAML” 视图中,将 MainWindow.xaml 的代码替换为下面的代码。Replace the code in the XAML view of MainWindow.xaml with the following code.

    <Window x:Class="AsyncWarning.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="MainWindow" Height="350" Width="525">
        <Grid>  
            <Button x:Name="startButton" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="startButton_Click" />  
            <TextBox x:Name="resultsTextBox" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/>  
        </Grid>  
    </Window>  
    

    MainWindow.xaml 的“设计” 视图中将显示一个简单的窗口,其中包含一个按钮和一个文本框。A simple window that contains a button and a text box appears in the Design view of MainWindow.xaml.

    有关 XAML 设计器的详细信息,请参阅使用 XAML 设计器创建 UIFor more information about the XAML Designer, see Creating a UI by using XAML Designer. 有关如何自行生成简单 UI 的信息,请参阅“创建 WPF 应用程序的步骤”和“设计简单 WPF MainWindow 的步骤”两个部分,这两个部分位于演练:使用 Async 和 Await 访问 WebFor information about how to build your own simple UI, see the "To create a WPF application" and "To design a simple WPF MainWindow" sections of Walkthrough: Accessing the Web by Using Async and Await.

  4. 将 MainWindow.xaml.cs 中的代码替换为以下代码。Replace the code in MainWindow.xaml.cs with the following code.

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Text;  
    using System.Threading.Tasks;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Data;  
    using System.Windows.Documents;  
    using System.Windows.Input;  
    using System.Windows.Media;  
    using System.Windows.Media.Imaging;  
    using System.Windows.Navigation;  
    using System.Windows.Shapes;  
    
    namespace AsyncWarning  
    {  
        public partial class MainWindow : Window  
        {  
            public MainWindow()  
            {  
                InitializeComponent();  
            }  
    
            private async void startButton_Click(object sender, RoutedEventArgs e)  
            {  
                resultsTextBox.Text += "\r\nEntering the Click event handler.";  
                await CallingMethodAsync();  
                resultsTextBox.Text += "\r\nExiting the Click event handler.";  
            }  
    
            async Task CallingMethodAsync()  
            {  
                resultsTextBox.Text += "\r\n  Entering calling method.";  
                // Variable delay is used to slow down the called method so that you can  
                // distinguish between awaiting and not awaiting in the program's output.  
                // You can adjust the value to produce the output that this topic shows  
                // after the code.  
                var delay = 5000;  
    
                // Call #1.  
                // Call an async method. Because you don't await it, its completion
                // isn't coordinated with the current method, CallingMethodAsync.  
                // The following line causes warning CS4014.  
                CalledMethodAsync(delay);  
    
                // Call #2.  
                // To suppress the warning without awaiting, you can assign the
                // returned task to a variable. The assignment doesn't change how  
                // the program runs. However, recommended practice is always to  
                // await a call to an async method.  
    
                // Replace Call #1 with the following line.  
                //Task delayTask = CalledMethodAsync(delay);  
    
                // Call #3  
                // To contrast with an awaited call, replace the unawaited call
                // (Call #1 or Call #2) with the following awaited call. Best
                // practice is to await the call.  
    
                //await CalledMethodAsync(delay);  
    
                // If the call to CalledMethodAsync isn't awaited, CallingMethodAsync  
                // continues to run and, in this example, finishes its work and returns  
                // to its caller.  
                resultsTextBox.Text += "\r\n  Returning from calling method.";  
            }  
    
            async Task CalledMethodAsync(int howLong)  
            {  
                resultsTextBox.Text +=
                    "\r\n    Entering called method, starting and awaiting Task.Delay.";  
    
                // Slow the process down a little so that you can distinguish between  
                // awaiting and not awaiting in the program's output. Adjust the value  
                // for howLong if necessary.  
                await Task.Delay(howLong);  
                resultsTextBox.Text +=
                    "\r\n    Task.Delay is finished--returning from called method.";  
            }  
        }  
    
        // Output with Call #1 or Call #2. (Wait for the last line to appear.)  
    
        // Entering the Click event handler.  
        //   Entering calling method.  
        //     Entering called method, starting and awaiting Task.Delay.  
        //   Returning from calling method.  
        // Exiting the Click event handler.  
        //     Task.Delay is finished--returning from called method.  
    
        // Output with Call #3, which awaits the call to CalledMethodAsync.  
    
        // Entering the Click event handler.  
        //   Entering calling method.  
        //     Entering called method, starting and awaiting Task.Delay.  
        //     Task.Delay is finished--returning from called method.  
        //   Returning from calling method.  
        // Exiting the Click event handler.  
    }  
    
  5. 按 F5 键以运行程序,然后选择 “启动” 按钮。Choose the F5 key to run the program, and then choose the Start button.

预期的输出显示在代码末尾。The expected output appears at the end of the code.

请参阅See also