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
