I don\'t know if I am thinking in the wrong way about TPL, but I have difficulty understanding how to obtain the following:
I have two functions
Task<
While the accepted answer would probably work
Task<B> Combined()
{
Task<A> ta = getA();
Task<B> ttb = ta.ContinueWith(a => getB(a.Result)).Unwrap();
return ttb;
}
Is a much more elegant way to implement this.
In case you are familiar with LINQ (and the Monad concept behind it), then below is a simple Task monad which will allow you to compose the Tasks.
Monad implementation:
public static class TaskMonad
{
public static Task<T> ToTask<T>(this T t)
{
return new Task<T>(() => t);
}
public static Task<U> SelectMany<T, U>(this Task<T> task, Func<T, Task<U>> f)
{
return new Task<U>(() =>
{
task.Start();
var t = task.Result;
var ut = f(t);
ut.Start();
return ut.Result;
});
}
public static Task<V> SelectMany<T, U, V>(this Task<T> task, Func<T, Task<U>> f, Func<T, U, V> c)
{
return new Task<V>(() =>
{
task.Start();
var t = task.Result;
var ut = f(t);
ut.Start();
var utr = ut.Result;
return c(t, utr);
});
}
}
Usage example:
public static void Main(string[] arg)
{
var result = from a in getA()
from b in getB(a)
select b;
result.Start();
Console.Write(result.Result);
}
If you are unable to use await
, you can certainly use Unwrap
, but it handles exceptions sub-optimally. The method I prefer is Then
as described in this article. Composition becomes simple and elegant:
Task<B> Combined()
{
return getA().Then(getB);
}
For those interested in the details, I wrote a blog post a while ago about exactly this problem of composing asynchronous methods, and how monads provide an elegant solution.
Does your getB
have to be a method which returns Task<B>
rather than B
?
The problem is that ContinueWith is:
public Task<TNewResult> ContinueWith<TNewResult>(
Func<Task<TResult>, TNewResult> continuationFunction,
CancellationToken cancellationToken
)
So in your case, because getB
returns Task<B>
, you're passing in a Func<Task<A>, Task<B>>
, so TNewResult
is Task<B>
.
If you can change getB
to just return a B
given an A
, that would work... or you could use:
return ta.ContinueWith(a => getB(a.Result).Result);
Then the lambda expression will be of type, Func<Task<A>, B>
so ContinueWith
will return a Task<B>
.
EDIT: In C# 5 you could easily write:
public async Task<B> CombinedAsync()
{
A a = await getA();
B b = await getB(a);
return b;
}
... so it's "just" a matter of working out what that ends up as. I suspect it's something like this, but with error handling:
public Task<B> CombinedAsync()
{
TaskCompletionSource<B> source = new TaskCompletionSource();
getA().ContinueWith(taskA => {
A a = taskA.Result;
Task<B> taskB = getB(a);
taskB.ContinueWith(t => source.SetResult(t.Result));
});
return source.Task;
}
Does that make sense?