Run sequence of tasks, one after the other

.net, asynchronous, c#, c#-4.0

Solution

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.

Problem

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 output of the end of the sequence. (If the tasks didn't depend on one another then I could do it in parallel and I would use `TaskFactory.ContinueWhenAll`.) I'd like to be able to implement this method: ``` static Task<TState> AggregateAsync<T, TState>( IEnumerable<T> items, TState initial, Func<TState, T, Task<TState>> makeTask); ``` How can I efficiently run the tasks one after another in sequence? I'm using C# 4.0 so I can't use `async`/`await` to do it for me. Edit: I could write `AggregateAsync` like this: ``` static Task<TState> AggregateAsync<T, TState>(IEnumerable<T> items, TState initial, Func<TState, T, Task<TState>> makeTask) { var initialTask = Task.Factory.StartNew(() => initial); return items.Aggregate( initialTask, (prevTask, item) => { prevTask.Wait(); // synchronous blocking here? return makeTask(prevTask.Result, item); }); } ``` But surely I'll get a batch of tasks, each of which blocks synchronously waiting for the one before it?

Original source