C# 等待最后一个异步调用和仅仅返回它之间有区别吗?
见问题标题 换句话说,给定一种方法C# 等待最后一个异步调用和仅仅返回它之间有区别吗?,c#,async-await,C#,Async Await,见问题标题 换句话说,给定一种方法 async Task FrobnicateAsync() { ... } 两者之间有什么(可能是细微的)区别吗 async Task FrobAndFrobnicateAsync() { Frob(); await FrobnicateAsync(); } 及 如果您逐字地显示了代码,那么您应该更喜欢return FrobnicateAsync()版本-它避免了额外的状态机抽象层,而且效率更高 如果您有其他内容,特别是围绕返回的任何最终(包
async Task FrobnicateAsync() { ... }
两者之间有什么(可能是细微的)区别吗
async Task FrobAndFrobnicateAsync()
{
Frob();
await FrobnicateAsync();
}
及
如果您逐字地显示了代码,那么您应该更喜欢
return FrobnicateAsync()
版本-它避免了额外的状态机抽象层,而且效率更高
如果您有其他内容,特别是围绕返回
的任何最终
(包括使用
)代码,则必须确保使用等待
版本,以便在异步
任务完成后才发生最终
代码
如果有疑问,请使用async
/wait
版本
好消息是:在它们之间切换不会影响签名(因此不是一个突破性的更改),因此您现在可以更有效地实现它,如果您发现以后需要使用块添加一个,则可以更改为异步
/等待
另外一个想法是:如果您通常希望同步完成,那么您可以更微妙:
Task FrobAndFrobnicateAsync()
{
async Task Awaited(Task t) => await t;
Frob();
var task = FrobnicateAsync();
// in .NET vFuture there will be a task.IsCompletedSuccessfully
return task.State == TaskState.RanToCompletion ? task : Awaited(task);
}
这避免了在不需要时使用状态机我有一篇博客文章
由于在等待
之前有一个非常重要的Frob
,因此我建议您保持异步
和等待
。这种方法的好处是:
- 如果没有
async
和Frob
抛出异常,那么将直接抛出异常,这对于消费者来说是一种令人惊讶的行为。即使Frob今天没有抛出异常,它也可以在明年抛出异常;因此,带有wait
的代码更易于维护
- 如果没有
async
,则Frob
与FrobnicateAsync
在同一逻辑调用上下文中不存在(假设它实际上是async
);相反,它将与frobandfrobniteasync
的调用者存在于相同的逻辑调用上下文中。在大多数代码中,这不是一个问题
另请参见比尔·瓦格纳(Bill Wagner)昨天写的一篇文章,其中有一个潜在差异的例子。另请参见:我不确定我是否总是同意这一点-如果Frob
可能抛出异常,那么使其异步就有很大的不同。请看斯蒂芬·克利里(Stephen Cleary)关于这一点的博文:(在某些情况下,我认为这很好,在另一些情况下,没有那么多。)@JonSkeet,但你没有吸收其中的差异;调用者仍然应该是等待
-ing或其他什么,因此他们将以通常的方式在那里获得异常,但这可能在他们的代码中处于完全不同的点。如果这是一个“调用方代码中的错误”异常(例如验证),那么我对此没有意见-如果它类似于一个IOException
,那么更改抛出它的点感觉更像是一个突破性的更改。@JonSkeet啊,你是说调用FrobnicateAsync
之前FrobnicateAsync
中的异常?同意:这绝对是一个区别;讨论哪个版本(如果有的话)更正确是一个非常复杂的对话。就我个人而言,无论是哪种方式,我都可以接受。另一方面,在应用程序代码中,您更有可能了解调用者,并且能够在必要时改变他们对异常的期望:)
Task FrobAndFrobnicateAsync()
{
async Task Awaited(Task t) => await t;
Frob();
var task = FrobnicateAsync();
// in .NET vFuture there will be a task.IsCompletedSuccessfully
return task.State == TaskState.RanToCompletion ? task : Awaited(task);
}