private async Task<T> LoadForm(WebControlAsync browser, Uri url)
{ ... }
var forms = await await _dispatcher.InvokeAsync(async () => await LoadForm(browser, form.Url));
我不明白为什么我必须在await
使用两个T
来获取forms
中的InvokeAsync
?因此看起来Task<Task<T>>
会返回var forms = await _dispatcher.InvokeAsync(() => FillForm(forms, url));
。但是当我调用这样的同步方法时:
await
它只需要一个var forms = await await _dispatcher.InvokeAsync(() => LoadForm(browser, form.Url));
。所以原因似乎是异步lambda。我明白,如果我这样写:
LoadForm
然后Task<T>
的返回类型为InvokeAsync
,Task<lambda return type>
返回Task<Task<T>>
,因此它确实为Task
。但是当await
方法被Task
编辑时,是不是从var forms = await LoadForm(browser, form.Url);
“解开”了实际的返回类型?所以,如果我写:
forms
T
将是Task<T>
,而不是{{1}}。为什么在async lambda中没有发生同样的情况?
答案 0 :(得分:8)
你已经回答了自己的问题。 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
。除了调度程序线程超负荷工作之外,它不会给你太多。它返回的Task
通常会立即完成(无需等待Task
lambda创建的async
),只需再给你一层包装来处理。 LoadForm
已经async
,所以您不妨使用Dispatcher.Invoke
来async
SynchronizationContext
lambda,并最终返回您的任务LoadForm
想要等待,或者,如果您知道将始终使用正确的SynchronizationContext
(即在UI线程上)调用您的async/await
,请完全省略调度程序调用,并让{{1}做它的事。