为什么无效async坏?

时间:2017-08-01 21:28:32

标签: c# async-await

所以我理解为什么从异步中返回void通常没有任何意义,但我遇到了一种我认为完全有效的情况。考虑以下设计的例子:

protected override void OnLoad(EventArgs e)
{
    if (CustomTask == null)
        // Do not await anything, let OnLoad return.
        PrimeCustomTask();
}
private TaskCompletionSource<int> CustomTask;

// I DO NOT care about the return value from this. So why is void bad?
private async void PrimeCustomTask()
{
    CustomTask = new TaskCompletionSource<int>();
    int result = 0;
    try
    {
        // Wait for button click to set the value, but do not block the UI.
        result = await CustomTask.Task;
    }
    catch
    {
        // Handle exceptions
    }
    CustomTask = null;

    // Show the value
    MessageBox.Show(result.ToString());
}

private void button1_Click(object sender, EventArgs e)
{
    if (CustomTask != null)
        CustomTask.SetResult(500);
}

我意识到这是一个不寻常的例子,但我试图让它更简单,更通用。有人可以向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循惯例?

感谢您的帮助。

2 个答案:

答案 0 :(得分:15)

嗯,详细介绍"avoid async void" article中的原因:

  • 异步void方法具有不同的错误处理语义。逃离PrimeCustomTask的例外将非常难以处理。
  • 异步void方法具有不同的组合语义。这是一个以代码可维护性和重用为中心的论据。从本质上讲,PrimeCustomTask中的逻辑就在那里,就是它 - 它不能被组合成更高级别的async方法。
  • 异步void方法很难测试。从前两点开始,很难编写覆盖PrimeCustomTask的单元测试(或任何调用它的单元测试)。

同样重要的是要注意async Task是自然的方法。在several languages that have adopted async/await中,C#/ VB是唯一支持async void的AFAIK。 F#没有,Python没有,JavaScript和TypeScript没有。从语言设计的角度来看,async void是不自然的。

async void添加到C#/ VB的原因是启用异步事件处理程序。如果您更改代码以使用async void事件处理程序:

protected override async void OnLoad(EventArgs e)
{
  if (CustomTask == null)
    await PrimeCustomTask();
}

private async Task PrimeCustomTask()

然后async void的缺点仅限于您的事件处理程序。特别是,来自PrimeCustomTask的异常自然地传播到其(异步)调用者(OnLoad),PrimeCustomTask可以组成(从其他异步方法自然调用),PrimeCustomTask在单元测试中更容易包含。

答案 1 :(得分:7)

使用void async只是通常被视为“坏”,因为:

  • 你不能等待它的完成(已在本文中提到)
  • 任何未处理的异常都将终止您的流程(哎哟!)

有很多案例(比如你的)使用它很好。使用时要小心。