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:
DoInt()
or DoString()
, so I can't
modify them.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.
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.
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().
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.
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.
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);
}
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