A .NET Task represents a unit of work that can complete later. It does not necessarily run on a separate thread: it may run on the same thread. Tasks are useful for network and disk operations, where a program would otherwise spend time waiting. section 4.4
An async method can return Task<Comic?> while awaiting an HTTP response and JSON deserialization. Pass a CancellationToken explicitly to asynchronous APIs so the work can respond to a request to stop. section 4.4
Listing 4.34 Modifying GetComic to be asynchronous
public static async Task<Comic?> GetComicAsync( #1
int number, #2
CancellationToken cancellationToken) #3
{
if (cancellationToken.IsCancellationRequested) #4
{
return null;
}
try
{
var path = number == 0 ? "info.0.json"
: $"{number}/info.0.json";
var stream = await client.GetStreamAsync( #5
path, cancellationToken); #6
return await JsonSerializer.DeserializeAsync<Comic>( #7
stream, cancellationToken: cancellationToken); #6
}
catch (AggregateException e)
when (e.InnerException is HttpRequestException)
{
return null;
}
catch (HttpRequestException)
{
return null;
}
catch (TaskCanceledException) #8
{
return null;
}
}
The method returns a Task, awaits network and deserialization calls, and passes a cancellation token to both. section 4.4
For a title search, starting multiple comic requests avoids waiting for each HTTP call before making the next. The calling code awaits the asynchronous search result. 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 starts comic requests and keeps track of their tasks while looking for a match. section 4.4









