为什么无效的异步不好?
所以我理解为什么从异步中返回void通常没有任何意义,但是我遇到了一种我认为完全有效的情况。 考虑以下人为的例子:
protected override void OnLoad(EventArgs e) { if (CustomTask == null) // Do not await anything, let OnLoad return. PrimeCustomTask(); } private TaskCompletionSource CustomTask; // I DO NOT care about the return value from this. So why is void bad? private async void PrimeCustomTask() { CustomTask = new TaskCompletionSource(); 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); }
我意识到这是一个不寻常的例子,但我试图让它更简单,更通用。 有人可以向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循约定?
谢谢你的帮助。
好吧,通过“避免async void
”文章中的原因:
- 异步void方法具有不同的error handling语义。 逃离
PrimeCustomTask
exception处理起来非常尴尬。 - 异步void方法具有不同的组合语义。 这是一个以代码可维护性和重用为中心的论据。 从本质上讲,
PrimeCustomTask
的逻辑就在那里,就是它 – 它不能组成更高级别的async
方法。 - 异步void方法很难测试。 自然从前两点开始,编写一个覆盖
PrimeCustomTask
(或任何调用它)的unit testing非常困难。
同样重要的是要注意async Task
是自然的方法。 在采用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
exception自然地传播到它的(异步)调用者( OnLoad
), PrimeCustomTask
可以被组合(从其他异步方法自然地调用),并且PrimeCustomTask
更容易包含在unit testing中。
使用void async通常只被视为“坏”,因为:
- 你不能等待它的完成(已在本文中提到)
- 任何未处理的exception将终止您的过程(哎哟!)
有很多案例(比如你的)使用它很好。 使用它时要小心。