Is there a callback when the task is completed in Task.WhenAll

Suppose I have the following:

IEnumerable<Task<TimeSpan>> tasks = //... TimeSpan[] results = await Task.WhenAll(tasks); // Handle results 

By the time I can process the results, all tasks must be completed.

Is there a way to handle each result on demand?

Similar to registering a delegation / callback that will be executed after the task completes:

 IEnumerable<Task<TimeSpan>> tasks = //... await Task.WhenAll(tasks, result => { // A task has finished. This will get executed. // result is of type TimeSpan }); 
+6
source share
2 answers

Is there a way to handle each result on demand?

Yes, you use WhenAny instead of WhenAll ... or call ContinueWith for each task.

For example, for the WhenAny approach:

 ISet<Task<TimeSpan>> tasks = new HashSet<Task<TimeSpan>>(...); while (tasks.Count != 0) { var task = await Task.WhenAny(tasks); // Use task here tasks.Remove(task); } 

There is another option that you could use when you convert the original task sequence to a task sequence that runs in order but gives the same results. Details are in this blog post , but as a result, you can use:

 foreach (var task in tasks.InCompletionOrder()) { var result = await task; // Use the result } 
+5
source

Is there a way to handle each result on demand?

Similar to registering a delegation / callback that will be executed when the task completes

Yes, you just need to tweak your thinking a bit.

Forget registering callbacks ( ContinueWith is a dangerous, extremely low level API ). In addition, you almost never have to order tasks upon completion. Instead, think about your problem in terms of operations (tasks).

You now have a set of tasks that return a TimeSpan . Each item in this collection is the only operation that returns a TimeSpan . What you really want to do is the concept of a single higher-level operation that waits for the initial operation to complete and then executes your logic after the operation.

This is exactly what async / await for:

 private static async Task<TimeSpan> HandleResultAsync(Task<TimeSpan> operation) { var result = await operation; // A task has finished. This will get executed. // result is of type TimeSpan ... return result; // (assuming you want to propagate the result) } 

Now you want to apply this higher level operation to your existing operations. LINQ Select perfect for this:

 IEnumerable<Task<TimeSpan>> tasks = ... IEnumerable<Task<TimeSpan>> higherLevelTasks = tasks.Select(HandleResultAsync); TimeSpan[] results = await Task.WhenAll(higherLevelTasks); // By the time you get here, all results have been handled individually. 

If you do not need a final collection of results, this can be further simplified:

 private static async Task HandleResultAsync(Task<TimeSpan> operation) { var result = await operation; // A task has finished. This will get executed. // result is of type TimeSpan ... } IEnumerable<Task<TimeSpan>> tasks = ... IEnumerable<Task> higherLevelTasks = tasks.Select(HandleResultAsync); await Task.WhenAll(higherLevelTasks); 
+5
source

All Articles