[英]Double await when invoke async lambda via Dispatcher
private async Task<T> LoadForm(WebControlAsync browser, Uri url)
{ ... }
var forms = await await _dispatcher.InvokeAsync(async () => await LoadForm(browser, form.Url));
我不明白为什么我必须在这里使用两个await
来获取forms
T
? 因此看起来InvokeAsync
返回Task<Task<T>>
。 但是当我调用这样的同步方法时:
var forms = await _dispatcher.InvokeAsync(() => FillForm(forms, url));
它只需要一个await
。 所以原因似乎是异步lambda。 我明白,如果我这样写:
var forms = await await _dispatcher.InvokeAsync(() => LoadForm(browser, form.Url));
然后LoadForm
的返回类型是Task<T>
, InvokeAsync
返回Task<lambda return type>
所以它确实是Task<Task<T>>
。 但是当一个Task
方法await
'ed'时,是不是“从” Task
“解开”实际的返回类型? 所以,如果我写:
var forms = await LoadForm(browser, form.Url);
forms
将是T
,而不是Task<T>
。 为什么在异步lambda中不会发生同样的情况?
你已经回答了自己的问题。 InvokeAsync
返回一个Task<T>
,其中T
是提供给它的Func<TResult>
委托的返回类型。 当您使用异步lambda时,您不再处理Func<TResult>
,而是处理Func<Task<TResult>>
。
您认为解包应该自动发生的原因可能是由于过去使用Task.Run
。 但应该注意的是, Task.Run
有一个重载接受一个Func<T>
并返回一个Task<T>
,一个重载接受一个Func<Task<T>>
, 仍然返回Task<T>
,所以你把这个在封面后面发生的事情解开是理所当然的。 但是,这不适用于您的情况。
在这方面, Dispatcher.InvokeAsync
类似于Task.Factory.StartNew
。 它没有专门处理Func<Task<TResult>>
的重载,所以你不得不“手动”解包。
InvokeAsync
是,考虑一下你的场景中是否真的需要InvokeAsync
。 除了调度程序线程超负荷工作之外,它不会给你太多。 它返回的Task
通常会立即完成(无需等待async
lambda创建的Task
),只需再给你一层包装来处理。 LoadForm
已经是async
所以你也可以使用Dispatcher.Invoke
,它将在正确的SynchronizationContext
上触发你的async
lambda,并最终返回你想要等待的任务,或者,如果你知道你的LoadForm
将始终被调用对于SynchronizationContext
(也就是说,在UI线程上),完全省略调度程序调用,让async/await
做它的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.