Sequential processing of asynchronous tasks

Just for the sake of completeness, that's how I would implement the helper method suggested by Chris Sinclair:

public void RunSequential(Action onComplete, Action<Exception> errorHandler,
                          params Func<Task>[] actions)
{
    RunSequential(onComplete, errorHandler,
                  actions.AsEnumerable().GetEnumerator());
}

public void RunSequential(Action onComplete, Action<Exception> errorHandler,
                          IEnumerator<Func<Task>> actions)
{
    if(!actions.MoveNext())
    {
        onComplete();
        return;
    }

    var task = actions.Current();
    task.ContinueWith(t => errorHandler(t.Exception),
                      TaskContinuationOptions.OnlyOnFaulted);
    task.ContinueWith(t => RunSequential(onComplete, errorHandler, actions),
                      TaskContinuationOptions.OnlyOnRanToCompletion);
}

This ensures that each subsequent task is only requested when the previous one completed successfully.
It assumes that the Func<Task> returns an already running task.


Here's how it would work with async:

try
{
    await FooAsync();
    await BarAsync();
    await FubarAsync();
    Console.WriteLine("All done");
}
catch(Exception e) // For illustration purposes only. Catch specific exceptions!
{
    Console.WriteLine(e);
}

This would work on .NET 4.0 if you installed the (prerelease) Microsoft.Bcl.Async package.


Since you're stuck on VS2010, you can use a variant of Stephen Toub's Then:

public static Task Then(this Task first, Func<Task> next)
{
  var tcs = new TaskCompletionSource<object>();
  first.ContinueWith(_ =>
  {
    if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions);
    else if (first.IsCanceled) tcs.TrySetCanceled();
    else
    {
      try
      {
        next().ContinueWith(t =>
        {
          if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
          else if (t.IsCanceled) tcs.TrySetCanceled();
          else tcs.TrySetResult(null);
        }, TaskContinuationOptions.ExecuteSynchronously);
      }
      catch (Exception exc) { tcs.TrySetException(exc); }
    }
  }, TaskContinuationOptions.ExecuteSynchronously);
  return tcs.Task; 
}

You can use it as such:

var task = FooAsync().Then(() => BarAsync()).Then(() => FubarAsync());
task.ContinueWith(t =>
{
  if (t.IsFaulted || t.IsCanceled)
  {
    var e = t.Exception.InnerException;
    // exception handling
  }
  else
  {
    Console.WriteLine("All done");
  }
}, TaskContinuationOptions.ExcecuteSynchronously);

Using Rx, it would look like this (assuming you don't have the async methods already exposed as IObservable<Unit>):

FooAsync().ToObservable()
    .SelectMany(_ => BarAsync().ToObservable())
    .SelectMany(_ => FubarAsync().ToObservable())
    .Subscribe(_ => { Console.WriteLine("All done"); },
        e => { Console.WriteLine(e); });

I think. I'm not an Rx master, by any means. :)