Skip to content

Instantly share code, notes, and snippets.

@prabirshrestha
Created January 7, 2012 10:19
Show Gist options
  • Save prabirshrestha/1574348 to your computer and use it in GitHub Desktop.
Save prabirshrestha/1574348 to your computer and use it in GitHub Desktop.
TaskIterationExtensions
namespace TaskIterationExtensions
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <remarks>
// http://blogs.msdn.com/b/pfxteam/archive/2009/06/30/9809774.aspx
/// </remarks>
static class TaskIterationExtensions
{
public static Task Iterate(this TaskFactory factory, IEnumerable<Task> asyncIterator)
{
if (factory == null)
throw new ArgumentNullException("factory");
if (asyncIterator == null)
throw new ArgumentNullException("asyncIterator");
// Get the scheduler to use, either the one provided by the factory
// or the current one if the factory didn’t have one specified.
var scheduler = factory.Scheduler ?? TaskScheduler.Current;
// Get an enumerator from the enumerable
var enumerator = asyncIterator.GetEnumerator();
if (enumerator == null) throw new InvalidOperationException();
// Create the task to be returned to the caller. And ensure
// that when everything is done, the enumerator is cleaned up.
var trs = new TaskCompletionSource<object>(factory.CreationOptions);
trs.Task.ContinueWith(_ => enumerator.Dispose(), scheduler);
// This will be called every time more work can be done.
Action<Task> recursiveBody = null;
recursiveBody = antecedent =>
{
try
{
// If the previous task completed with any exceptions, bail
if (antecedent != null && antecedent.IsFaulted)
trs.TrySetException(antecedent.Exception);
// If the user requested cancellation, bail.
else if (trs.Task.IsCanceled) trs.TrySetCanceled();
// If we should continue iterating and there's more to iterate
// over, create a continuation to continue processing. We only
// want to continue processing once the current Task (as yielded
// from the enumerator) is complete.
else if (enumerator.MoveNext())
enumerator.Current.ContinueWith(recursiveBody, scheduler);
// Otherwise, we're done!
else trs.TrySetResult(null);
}
// If MoveNext throws an exception, propagate that to the user
catch (Exception exc) { trs.TrySetException(exc); }
};
// Get things started by launching the first task
factory.StartNew(() => recursiveBody(null));
// Return the representative task to the user
return trs.Task;
}
public static Task Iterate(this TaskFactory factory, params Func<Task>[] taskFunctions)
{
return Iterate(factory, FuncTasksToEnumerable(taskFunctions));
}
private static IEnumerable<Task> FuncTasksToEnumerable(Func<Task>[] taskFunctions)
{
foreach (var taskFunction in taskFunctions)
yield return taskFunction();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment