是否存在任何方案,其中编写方法如下所示:
public async Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return await DoAnotherThingAsync();
}
而不是这样:
public Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return DoAnotherThingAsync();
}
会有意义吗
- 当您可以直接从内部
DoAnotherThingAsync()
调用返回Task<T>
时,为什么要使用return await
构造?**
- 当您可以直接从内部
我在很多地方看到了return await
的代码,我想我可能错过了一些东西。但据我所知,在这种情况下不使用async/await关键字,直接返回任务在功能上是等效的。为什么要增加额外的await
层的额外开销呢?
9条答案
按热度按时间wa7juj8i1#
有一种狡猾的情况,普通方法中的
return
和async
方法中的return await
表现不同:当与using
(或更一般地,try
嵌段中的任何return await
)组合时。考虑以下两种方法:
第一个方法在
DoAnotherThingAsync()
方法返回后立即Dispose()
Foo
对象,这可能在它实际完成之前很久,这意味着第一个版本可能有bug(因为Foo
处理得太早),而第二个版本将工作正常。sq1bmfud2#
如果不需要
async
(即可以直接返回Task
),则不要使用async
。在某些情况下,
return await
非常有用,例如,如果您有 * 两个 * 异步操作要执行:有关
async
性能的更多信息,请参见Stephen Toub的MSDN article和video主题。**更新:**我已经编写了一个blog post,其中包含了更多细节。
anauzrmj3#
你想要这么做的唯一原因是如果在前面的代码中有其他的
await
,或者你在返回结果之前以某种方式操纵它。另一种可能发生的方式是通过try/catch
来改变异常的处理方式。如果你没有这样做,那么你是对的。所以没有理由增加生成async
方法的开销。ffdz8vbo4#
另一个你可能需要等待结果的例子是:
在这种情况下,
GetIFooAsync()
必须等待GetFooAsync
的结果,因为T
的类型在两个方法之间是不同的,并且Task<Foo>
不能直接赋值给Task<IFoo>
。它就变成了Foo
,它 * 是 * 可以直接赋值给IFoo
的,然后async方法就把结果重新打包到Task<IFoo>
中,然后你就可以走了。juud5qan5#
如果你不使用return await,你可能会在调试时或者在异常日志中打印堆栈跟踪时破坏堆栈跟踪。
当你返回任务时,这个方法完成了它的任务,它离开了调用堆栈。当你使用
return await
时,你把它留在了调用堆栈中。例如:
使用await时调用堆栈:A等待B的任务=〉B等待C的任务
62lalag46#
将简单的“thunk”方法异步化会在内存中创建一个异步状态机,而非异步状态机则不会。然而,这通常会让人们使用非异步版本,因为它更高效(这是真的)这也意味着在挂起的情况下,你没有证据表明那个方法涉及到“返回/继续堆栈”,这有时会使理解挂起变得更加困难。
所以,是的,当perf不是关键的时候(通常也不是),我会在所有这些thunk方法上抛出async,这样我就有了async状态机来帮助我在以后诊断挂起,也帮助确保如果这些thunk方法随着时间的推移而发展,它们一定会返回错误的任务,而不是抛出。
xu3bshqb7#
这也使我感到困惑,我觉得前面的回答忽略了你的实际问题:
既然可以直接从内部DoAnotherThingAsync()调用返回Task,为什么还要使用return await构造呢?
当然,有时候您实际上需要一个
Task<SomeType>
,但大多数情况下,您实际上需要SomeType
的一个示例,即任务的结果。从您的代码:
不熟悉语法的人(比如我)可能会认为这个方法应该返回一个
Task<SomeResult>
,但是因为它被标记为async
,这意味着它实际的返回类型是SomeResult
。如果你只使用return foo.DoAnotherThingAsync()
,你将返回一个任务,这将不会编译。正确的方法是返回任务的结果,所以return await
。hvvq6cgz8#
另一个可能需要
return await
的原因是:使用await
语法可以避免Task<T>
和ValueTask<T>
类型不匹配。例如,即使SubTask方法返回Task<T>
,但其调用者返回ValueTask<T>
,下面的代码也能正常工作。如果跳过
DoSomething()
行上的wait,将得到编译器错误CS0029:无法将类型“System.Threading.Tasks.Task”隐式转换为“System.Threading.Tasks.ValueTask”。
如果尝试显式地对CS0030进行类型转换,也会得到CS0030。
顺便说一句,这是.NET框架。我完全可以预见会有评论说“这在.NET * hypothetic_version* 中得到修复",我还没有测试过它。:)
vzgqcmou9#
non-await方法的另一个问题是有时候你不能隐式地转换返回类型,特别是对于
Task<IEnumerable<T>>
:错误:
无法将类型"System. Threading. Tasks. Task"隐式转换为"System. Threading. Tasks. Task" <System.Collections.Generic.List>' to 'System.Threading.Tasks.Task <System.Collections.Generic.IEnumerable>'