Run sequence of tasks, one after the other

前端 未结 2 1642
醉梦人生
醉梦人生 2020-12-09 13:33

I have a sequence of tasks, where each one depends on the output of the previous one. I\'d like to represent this as a single Task object, whose result is the o

相关标签:
2条回答
  • 2020-12-09 14:05

    The easy way (using Microsoft.Bcl.Async):

    static async Task<TState> AggregateAsync<T, TState>(
        this IEnumerable<T> items,
        TState initial,
        Func<TState, T, Task<TState>> makeTask)
    {
      var state = initial;
      foreach (var item in items)
        state = await makeTask(state, item);
      return state;
    }
    

    The hard way:

    static Task<TState> AggregateAsync<T, TState>(
        this IEnumerable<T> items,
        TState initial,
        Func<TState, T, Task<TState>> makeTask)
    {
      var tcs = new TaskCompletionSource<TState>();
      tcs.SetResult(initial);
      Task<TState> ret = tcs.Task;
      foreach (var item in items)
      {
        var localItem = item;
        ret = ret.ContinueWith(t => makeTask(t.Result, localItem)).Unwrap();
      }
      return ret;
    }
    

    Note that error handling is more awkward with the "hard" way; an exception from the first item will be wrapped in an AggregateException by each successive item. The "easy" way does not wrap exceptions like this.

    0 讨论(0)
  • 2020-12-09 14:06

    You can use Task.ContinueWith. The task you see in the code below, represents the previous (completed) task, and you can fetch its result to execute the second task, and so on.

    T item1 = default(T);
    T item2 = default(T);
    Task<TState> task1 = makeTask(initial, item1);
    
    //create second task
    task1.ContinueWith(task => makeTask(task.Result, item2).Result,
                         TaskContinuationOptions.OnlyOnRanToCompletion);
    

    Edit

    Sorry, I missed this part

    I'd like to represent this as a single Task object, whose result is the output of the end of the sequence.

    In order to do that, you just have to return a reference to the result of the last ContinueWith call.

    Task<State> aggregate = task1.ContinueWith(
                                      task => makeTask(task.Result, item2).Result,
                                      TaskContinuationOptions.OnlyOnRanToCompletion);
    
    var res = aggregate .Result; //wait synchronously for the result of the sequence
    
    0 讨论(0)
提交回复
热议问题