Chaining two functions () -> Task and A->Task

前端 未结 4 1614
你的背包
你的背包 2021-02-08 13:33

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<         


        
相关标签:
4条回答
  • 2021-02-08 13:49

    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.

    0 讨论(0)
  • 2021-02-08 13:50

    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);
            }
    
    0 讨论(0)
  • 2021-02-08 13:55

    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.

    0 讨论(0)
  • 2021-02-08 14:08

    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?

    0 讨论(0)
提交回复
热议问题