I have a Task<T> t1
. I want to run another Task t2
after t1
completes. I choose to use the .ContinueWith
method of t1
.
void ThenFrob(Task<Frobber> t1) {
t1.ContinueWith(frobber => frobber.Frob())
}
Except, I cannot do this, because the Action parameter of Task<T>
is passed the Task<T>
, rather than T
itself. Instead, I have to take the result of the parameter passed into my action to interact with it.
void ThenFrob(Task<Frobber> t1) {
t1.ContinueWith(frobberTask => {
var frobber = frobberTask.Result;
frobber.frob();
});
}
If the point of ContinueWith is to add another action to the chain, why wouldn't the language designers have simply passed the result of the previous task? Or, in the case of a non-generic Task expected a parameterless action?
ContinueWith runs the delegate when the task enters the task.IsCompleted == true
state. Not when it enters the task.IsCompleted == true && task.IsFaulted == false && task.IsCanceled == false
state, a task that threw a exception or a task that was canceled both get "completed" but will not produce a result.
There are overloads you can pass in TaskContinuationOptions
like TaskContinuationOptions.OnlyOnRanToCompletion
to get behavior like you are describing, but it would be more complex to have a Action<T>
overload for just that single enum option so they just use the general overload of Action<Task<T>>
so you can use the same method if you are doing OnlyOnRanToCompletion
or if you are doing OnlyOnFaulted
.
Also there is still useful information in a Task
object of a completed task, if you are using the AsyncState
property to pass metadata along if the completed task was not passed on to ContinueWith
you would not have a way to get that data unless you used variable capture of a lambada expression.