Reputation: 29421
Consider the following method:
private async Task<Task<Response>> SendAsync(string data)
{
this.Tcs = new TaskCompletionSource<Response>();
await this.Stream.WriteAsync(...);
await this.Stream.FlushAsync();
return this.Tcs.Task;
}
I have an async method, which I expect to return Task<Response>
. But since I want to return TaskCompletionSource<Response>
(which gets set elsewhere, so I can't await it here), I have to actually return Task<Task<Response>>
instead.
In the calling code, I have two ways of dealing with it while hiding this ugliness from the outside of the class. Assuming the response is not important and can be ignored, I can simply return a Task
:
public Task GetAsync(string key)
{
return this.SendAsync("GET " + key);
}
On the other hand, if I want the response, I have to use this ugly await await
to make it work:
public async Task<Response> GetAsync(string key)
{
return await await this.SendAsync("GET " + key);
}
Is there a better way of dealing with this, i.e. returning the Task<Response>
from SendAsync()
without exposing a Task<Task<Response>>
outside the class, and at the same time not using the await await
?
Upvotes: 1
Views: 91
Reputation: 19416
The answer by @i3arnon is a good solution, however another solution is to use the Unwrap
extension method.
The TaskExtensions.Unwrap
method is designed for converting a Task<Task<TResult>>
into a Task<TResult>
and can be used as follows:
public Task<Response> GetAsync(string key)
{
return this.SendAsync("GET " + key).Unwrap();
}
Any result, exception or cancellation will be propagated correctly to the resulting Task<TResult>
.
Upvotes: 1
Reputation: 116548
I'm not sure why you need to use a TaskCompletionSource
inside an async method. Usually you either do the one or the other.
But if you must then forget returning the TaskCompletionSource.Task
. Simply await the task like you do the rest of the async methods (WriteAsync
and FlushAsync
) and change the method to return Task<Response>
:
private async Task<Response> SendAsync(string data)
{
this.Tcs = new TaskCompletionSource<Response>();
await this.Stream.WriteAsync(...);
await this.Stream.FlushAsync();
return await this.Tcs.Task;
}
That way the async method returns a task that gets completed when there's a Response
so you only need to await SendAsync("...")
once.
Upvotes: 1