Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuing a Task<T> to a Task<U> without blocking on Result

I have two asynchronous methods Task<int> DoInt() and Task<string> DoString(int value). I have a third asynchronous method Task<string> DoBoth(int value) whose goal is to asynchronously execute DoInt(), piping it's output to DoString(int) and having the result be the result of DoBoth().

The important constraints are:

  1. I may not have source code to DoInt() or DoString(), so I can't modify them.
  2. I don't want to block at any point
  3. Output (result) of one task must be passed as input to the next
  4. The final output (result) is what I want to be the result of DoBoth()

The key being I already have methods that are asynchronous (i.e. return Task) and I want to pipe data from task to task, without blocking along the way, returning the final result in the initial task. I can do all the following except not blocking in the following code:

Code example:

// Two asynchronous methods from another library, i.e. can't be changed
Task<int> DoInt(); 
Task<string> DoString(int value);

Task<string> DoBoth()
{
    return DoInt().ContinueWith<string>(intTask => 
    {
        // Don't want to block here on DoString().Result
        return DoString(intTask.Result).Result; 
    });
}

Since Task<string> DoString(int) is already an asynchronous method, how do I cleanly create a non-blocking continuation to it? Effectively I want to create a continuation from an existing Task and not from a Func.

like image 848
MikeJansen Avatar asked Aug 14 '12 15:08

MikeJansen


People also ask

What is a continuation Task?

A continuation task (also known just as a continuation) is an asynchronous task that's invoked by another task, known as the antecedent, when the antecedent finishes.

Does task result block?

Wait and Task. Result are blocking and may also cause deadlocks and on top of that they also wrap exceptions in an AggregateException . Now if you are in a situation where you can't use async/await and you have to do sync over async, the preferred way to do it seems to be Task. GetAwaiter().

What happens if task is not awaited?

If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.

What is async await in C#?

An async keyword is a method that performs asynchronous tasks such as fetching data from a database, reading a file, etc, they can be marked as “async”. Whereas await keyword making “await” to a statement means suspending the execution of the async method it is residing in until the asynchronous task completes.


1 Answers

You can write the entire chain using TaskExtensions.Unwrap as:

Task<string> DoBoth(int value)
{
    Task<Task<string>> task = DoInt(value).ContinueWith(valueTask => 
    {
        return DoString(valueTask.Result); 
    });

    return task.Unwrap();
}

Note that this assumes that DoInt is defined as Task<int> DoInt(int value);, which differs from your description, but follows your code example.

If DoInt does not take an int argument (matching your declarations), this could become:

Task<string> DoBoth()
{
    return DoInt().ContinueWith(t => DoString(t.Result)).Unwrap();
}

As an extra - using C# 5 and .NET 4.5 (or the async targeting pack), you can write this as:

async Task<string> DoBoth(int value)
{
    int first = await DoInt(value);
    return await DoString(first);
}
like image 136
Reed Copsey Avatar answered Oct 02 '22 14:10

Reed Copsey