Let's say I have two independent async functions (that I don't control) that create Task objects:
Task A();
Task B();
and some other non-async function
void X();
How do I construct a single Task chain that executes all of these in sequence and allows further continuations (that will execute after X) to be appended?
If I do this:
Task Sequential()
{
return A()
.ContinueWith(t => { B(); })
.ContinueWith(t => { X(); });
}
that does not work, because B will start a new Task chain. If B takes a long time to complete, X will be executed first (along with whatever else the caller of Sequential might ContinueWith on the returned Task). I need X to be the last element in a single Task chain, with the B Task as its precedent.
If I do this:
Task Sequential()
{
return A()
.ContinueWith(t => { B().ContinueWith(t => { X(); }); });
}
that only partially solves the problem, because even though A, B and X will now execute in sequential order, if the caller does Sequential().ContinueWith, that continuation will execute in parallel to B and X, not after X.
Is there any reason not to use await? For example,
async Task Sequential()
{
await A();
await B();
X();
}
Assuming that you cannot use async/await
as suggested in other answers (if you can, you should), there's a nifty little extension method available to cater for this scenario since the introduction of Task
in .NET 4.0: System.Threading.Tasks.TaskExtensions.Unwrap
. It takes in a Task<Task>
(or Task<Task<TResult>>
) and "flattens" it into a contiguous Task
(or Task<TResult>
respectively), which represents the completion of both the outer task, and the inner task.
Using that extension method your method can be rewritten as:
Task Sequential()
{
return A()
.ContinueWith(t => B()).Unwrap()
.ContinueWith(t => X()); // You said X() is "non-async", so no Unwrap here.
}
The resultant Task
will represent the completion of the entire sequential chain of tasks, in the expected order.
There is also the concept of "child tasks" originally devised for this very purpose in the early days of the Task Parallel Library, but it is horrendously difficult to use and requires that you have great control over how the tasks are started, which you may not have. Still, it's worth knowing about (if only for education's sake).
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