308

没有这样写方法的场景:

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();
}

会有意义吗?

return await当您可以直接Task<T>从内部DoAnotherThingAsync()调用返回时,为什么要使用构造?

我在很多地方都看到了代码return await,我想我可能错过了一些东西。但据我了解,在这种情况下不使用 async/await 关键字并直接返回 Task 在功能上是等效的。为什么要增加额外await层的额外开销?

4

7 回答 7

229

return当在普通方法和方法return await中表现不同时,有一个偷偷摸摸的情况async:当与using(或更一般地,块return await中的任何try)结合时。

考虑这两个版本的方法:

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

第一个方法将Dispose()在方法返回后Foo立即返回对象DoAnotherThingAsync(),这可能在它实际完成之前很久。这意味着第一个版本可能有问题(因为Foo处理得太早了),而第二个版本可以正常工作。

于 2013-09-30T20:35:49.573 回答
119

如果你不需要async(即,你可以Task直接返回),那么不要使用async.

在某些情况下return await很有用,例如您有两个异步操作要做:

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

有关async性能的更多信息,请参阅 Stephen Toub 的MSDN 文章和有关该主题的视频。

更新:我写了一篇更详细的博客文章。

于 2013-09-30T15:35:06.607 回答
25

您想要这样做的唯一原因是await早期代码中是否有其他代码,或者您在返回结果之前以某种方式操作结果。另一种可能发生的方式是通过try/catch改变异常处理方式的方式。如果您没有做任何这些,那么您是对的,没有理由增加制作方法的开销async

于 2013-09-30T15:35:37.327 回答
19

您可能需要等待结果的另一种情况是:

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

在这种情况下,GetIFooAsync()必须等待结果,GetFooAsync因为T两个方法之间的类型不同,并且Task<Foo>不能直接分配给Task<IFoo>。但是如果你等待结果,它就变成Foo了 which is directly assignable to IFoo。然后,异步方法只是将结果重新打包,Task<IFoo>然后就可以离开了。

于 2015-05-08T15:30:26.960 回答
11

如果您不使用 return await 您可能会在调试时或在异常日志中打印时破坏您的堆栈跟踪。

当您返回任务时,该方法实现了它的目的,并且它不在调用堆栈中。当您使用时,return await您会将其留在调用堆栈中。

例如:

使用 await 时调用堆栈:A 等待 B 的任务 => B 等待 C 的任务

使用 await时调用堆栈:A 等待来自 C 的任务,而 B 已返回。

于 2019-01-16T06:16:45.043 回答
9

使原本简单的“thunk”方法异步在内存中创建一个异步状态机,而非异步则没有。虽然这通常可以指出人们使用非异步版本,因为它更有效(这是真的),但这也意味着在挂起的情况下,您没有证据表明该方法涉及“返回/继续堆栈”这有时会让人更难理解这个问题。

所以是的,当性能不重要时(通常不是),我将在所有这些 thunk 方法上抛出异步,以便我有异步状态机来帮助我稍后诊断挂起,并帮助确保如果那些thunk 方法会随着时间的推移而发展,它们肯定会返回错误的任务而不是 throw。

于 2015-05-08T15:34:37.107 回答
5

这也让我感到困惑,我觉得以前的答案忽略了你的实际问题:

当您可以从内部 DoAnotherThingAsync() 调用中直接返回 Task 时,为什么要使用 return await 构造?

好吧,有时您实际上想要一个Task<SomeType>,但大多数时候您实际上想要一个实例SomeType,即任务的结果。

从您的代码:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

不熟悉语法的人(比如我)可能会认为这个方法应该返回 a Task<SomeResult>,但是由于它被标记为async,这意味着它的实际返回类型是SomeResult。如果您只使用return foo.DoAnotherThingAsync(),您将返回一个无法编译的任务。正确的方法是返回任务的结果,所以return await.

于 2017-01-05T17:27:53.687 回答