private async Task<T> LoadForm(WebControlAsync browser, Uri url)
{ ... }
var forms = await await _dispatcher.InvokeAsync(async () => await LoadForm(browser, form.Url));
I don't understand why I have to use two await
's here to get T
in forms
? So it looks as InvokeAsync
returns Task<Task<T>>
. But when I invoke synchronous method like this:
var forms = await _dispatcher.InvokeAsync(() => FillForm(forms, url));
it requires only single await
. So the cause seems to be async lambda. I understand that if I write it this way:
var forms = await await _dispatcher.InvokeAsync(() => LoadForm(browser, form.Url));
then the return type of LoadForm
is Task<T>
and InvokeAsync
returns Task<lambda return type>
so it indeed would be Task<Task<T>>
. But when a Task
method is await
'ed, isn't it "unwraps" the actual return type from Task
? So if I write:
var forms = await LoadForm(browser, form.Url);
forms
would be T
, not Task<T>
. Why the same isn't happen in async lambda?
You've answered your own question. InvokeAsync
returns a Task<T>
where T
is the return type of the Func<TResult>
delegate supplied to it. When you use an async lambda, you are no longer dealing with Func<TResult>
, but instead Func<Task<TResult>>
.
The reason you think that unwrapping should happen automatically is probably due to using Task.Run
in the past. It should be noted, however, that Task.Run
has an overload which accepts a Func<T>
and returns a Task<T>
, and an overload which accepts a Func<Task<T>>
, and still returns Task<T>
, so you take this unwrapping which is happening for you behind the covers for granted. This, however, does not apply in your case.
Dispatcher.InvokeAsync
is similar to Task.Factory.StartNew
in this regard. It does not have an overload which would specifically deal with Func<Task<TResult>>
, so you're stuck with unwrapping "manually".
Thing is, think about whether you really need InvokeAsync
in your scenario. It won't give you much outside of scenarios where the dispatcher thread is overloaded with work. The Task
which it returns will generally complete immediately (without awaiting the Task
created by your async
lambda) and just give you one more layer of wrapping to deal with. LoadForm
is already async
so you might as well just use Dispatcher.Invoke
which will fire off your async
lambda on the correct SynchronizationContext
and ultimately return the task that you want to await, or, if you know that your LoadForm
will always be called with the right SynchronizationContext
(that is, on the UI thread), omit the dispatcher calls altogether and just let async/await
do its thing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With