有没有等效于异步关键字的方法?



有时,我想对异步方法的结果做一些事情,例如:

var foos = (await GetObjectAsync<IEnumerable<Foo>>()).ToList();

但是由于括号的原因,这种表示法几乎无法阅读。如果我使用结果调用另一个异步方法,则会嵌套多个await表达式,例如:

var result = (await (await GetObjectAsync<IEnumerable<Foo>>()).First().SomeMethodAsync()).GetResult();

我想写一个更流畅的等价物,例如:

var foos = GetObjectAsync<IEnumerable<Foo>>()
.Async()
.First()
.SomeMethodAsync()
.Async()
.GetResult();

我阅读了文档,但唯一具有正确签名(除非我错过了什么(的是ResultResult不是我想要的,因为它不等同于await.

有这样的方法吗?如果扩展不存在,我可以创建一个扩展来执行此操作吗?

您正在寻找ContinueWith

await GetObjectAsync<IEnumerable<Foo>>()
.ContinueWith(task => task.Result.ToList());

但是,这会表现得更糟,并且有一些可能需要一段时间才能理解的复杂性,例如确保为继续代码使用正确的调度程序(await为您执行此操作(。

如果可读性是一个问题,我宁愿将您的代码拆分为多行:

var foosEnumerable = await GetObjectAsync<IEnumerable<Foo>>();
var foos = foosEnumerable.ToList();

要获得流畅的语法,您可以使用以下扩展方法从Task<T>中创建一个函子和一个 monad:

public static async Task<TResult> Map<T, TResult>(this Task<T> source, Func<T, TResult> func)
{
var result = await source.ConfigureAwait(false);
return func(result);
}
public static async Task<TResult> Bind<T, TResult>(this Task<T> source, Func<T, Task<TResult>> func)
{
var result = await source.ConfigureAwait(false);
return await func(result).ConfigureAwait(false);
}

这允许您以方法链方式编写代码:

var resultTask = GetObjectAsync<IEnumerable<Foo>>()
.Map(foos => foos.First())
.Bind(foo => foo.SomeMethodAsync())
.Map(r => r.GetResult());

当然,我在这里不考虑性能,但对于代码的非性能关键部分,如果这有助于使您的代码更易于理解,我更喜欢可读性而不是微优化(在某些领域,我发现这种语法确实有帮助(。

还要记住,显然您仍然可以返回任务,因此在某些时候您将不得不等待它或阻止ResultWait.

方法链也可以通过ContinueWith实现@Cory如 Nelson 答案(小心地使用调度程序和选项的正确参数(避免创建异步状态机,但为了简单起见,我在扩展中使用了异步和等待,这是完全安全的。

最重要的是,如果将"映射"重命名为"选择"并"绑定到 SelectMany"(并添加带有投影的重载(,则还可以对任务使用 LINQ 查询语法,尽管这可能不会引起您的兴趣,因为您专门尝试从我可以读取的内容中摆脱该语法。

您可以使用

var foos = GetObjectAsync<IEnumerable<Foo>>().GetAwaiter().GetResult().ToList()

但这不一样!

如果使用"await"关键字,则在等待结果时,将处理Windows消息循环,并且不会冻结应用程序(因此将处理鼠标事件等(。

如果使用GetAwaiter().GetResult.Wait(),则线程在等待时被阻塞。

所以"等待"没有等价物。但是还有其他语法,看起来相似,但行为非常不同。

相关内容

最新更新