通过Dispatcher调用async lambda时使用Double await
本文关键字:Double await lambda Dispatcher 调用 async 通过 | 更新日期: 2023-09-27 18:12:35
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
。所以原因似乎是async 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>
。为什么同样的事情不会发生在async 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
。在dispatcher线程被工作过载的场景之外,它不会提供太多信息。它返回的Task
通常会立即完成(不需要等待由async
lambda创建的Task
),只是给您多一层包装来处理。LoadForm
已经是async
了,所以你不妨只使用Dispatcher.Invoke
,它会在正确的SynchronizationContext
上触发你的async
lambda,并最终返回你想要等待的任务,或者,如果你知道你的LoadForm
将始终被正确的SynchronizationContext
调用(即,在UI线程上),完全省略dispatcher调用,只是让async/await
做它的事情。