liveBook · Manning

Task.WhenAll

As explained in .NET in Action, Second Edition

Task.WhenAll is a .NET method that creates a task completing when all supplied tasks have completed.

Task.WhenAll creates a task that completes when every supplied task has completed. Use it to wait for a group of asynchronous operations rather than waiting for each result before starting the next operation. section 4.4

Searching comics by title

An asynchronous title search starts a GetComicAsync task for each comic number and collects the tasks. A continuation checks each result; on a match, a CancellationTokenSource requests cancellation of the other calls. Awaiting Task.WhenAll(tasks) waits for the collected work to complete. section 4.4

Inside the loop, copy number to localNumber before using it in an anonymous method. Capturing the changing loop variable can cause queued work to use the wrong number. section 4.4

Listing 4.35 Asynchronous version of GetComicWithTitle
static async Task<Comic?> GetComicWithTitleAsync(            #1
  string title)
{
  var cancellationToken = new CancellationTokenSource();     #2
  var lastComic = await Comic.GetComicAsync(0,               #3
    cancellationToken.Token);
  var tasks = new List<Task>();                              #4
  Comic? foundComic = null;                                  #5
  for (int number = lastComic!.Number;
       number > 0;
       number--)
  {
    var localNumber = number;                                #6
    var getComicTask = Comic.GetComicAsync(localNumber,      #7
      cancellationToken.Token);
    var continuationTask = getComicTask.ContinueWith(        #8
      t => {                                                 #9
        try
        {
          var comic = t.Result;
          if (comic != null &&
            string.Equals(title, comic!.Title,
            StringComparison.OrdinalIgnoreCase))
          {
            cancellationToken.Cancel();                      #10
            foundComic = comic;
          }
        }
        catch (TaskCanceledException) {}                     #11
      });
    tasks.Add(continuationTask);                             #12
  }
  
  await Task.WhenAll(tasks);                                 #13
  return foundComic;
}

The search listing shows task collection, a per-iteration localNumber, and a continuation that inspects each result. section 4.4

How .NET in Action, Second Edition covers it

How deep it goes
Shows task collection and continuations, with attention to loop-variable capture; does not detail Task.WhenAll internals.
Examples
Uses a comic-title search that creates GetComicAsync tasks and checks their results.
What it assumes
Assumes familiarity with C# loops and methods while explaining how anonymous methods capture a changing variable.
What you can do after
Apply it to wait for a collected set of asynchronous requests while avoiding a loop-variable capture error.
How it connects
Connects asynchronous methods, task continuations, cancellation tokens, HttpClient, and variable capture.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of .NET in Action, Second Edition. The section links point to the parts of the book it draws on.