Created
January 7, 2012 10:19
-
-
Save prabirshrestha/1574348 to your computer and use it in GitHub Desktop.
TaskIterationExtensions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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