Gigi
Gigi

Reputation: 29421

How do I hide Task of Task?

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

Answers (2)

Lukazoid
Lukazoid

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

i3arnon
i3arnon

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

Related Questions