liveBook · Manning

Async/await

As explained in .NET in Action, Second Edition

The await keyword asynchronously suspends a C# method until an awaitable operation completes and then resumes it.

A synchronous HTTP search waits for each request before continuing. In C#, async and await let the search use Tasks so multiple requests can be in progress at once. A Task represents work, but it does not necessarily run on a separate thread. 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;
  }
}

GetComicAsync returns Task<Comic?> and awaits both the HTTP stream and JSON deserialization. It passes a CancellationToken to each operation and handles request failures and cancellation. 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 title search starts comic-fetch Tasks while iterating over comic numbers, then attaches continuations to inspect their results. section 4.4

The caller must also await the asynchronous search. Measure elapsed time with Stopwatch, but treat a slow server or throttling as a possible influence on the result. section 4.4

How .NET in Action, Second Edition covers it

How deep it goes
Works through asynchronous fetch, deserialization, search, and caller code; defers remedies for slow responses and throttling.
Examples
Uses comic-fetch and title-search listings, an awaited command-line caller, and Stopwatch timing.
What you can do after
Apply it via the Task Parallel Library to issue overlapping HTTP requests and await asynchronous responses.
How it connects
Connects async/await to Tasks, the Task Parallel Library, HTTP requests, JSON deserialization, cancellation tokens, and continuations.

Also explained in

Compare how 17 books explain async/await →

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.