Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why await both the asynchronous request and the reading of its contents?

Why is it necessary in .NET Web API to have a a method that reads the content of an HTTP response asynchronously, given that there is already a method to make the request asynchronously? Said another way, if I am using HttpClient.GetAsync (or PostAsync, or PutAsync, etc), is the code really any more asynchronous by also reading the content out asynchronously? If so, how/why?

Is this:

using (var client = new HttpClient()) {
    var response = await client.GetAsync("...");
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsAsync<Foo>();
}

better than this:

using (var client = new HttpClient()) {
    var response = await client.GetAsync("...");
    response.EnsureSuccessStatusCode();
    return response.Content.ReadAsAsync<Foo>().Result;
}

and why?

like image 534
Keith Pinson Avatar asked Jun 27 '14 19:06

Keith Pinson


1 Answers

Why is it necessary in .NET Web API to have a a method that reads the content of an HTTP response asynchronously, given that there is already a method to make the request asynchronously?

Because it can. Those two methods do different things. The first send a network request over the wire, the second reads from the received Stream.

As both making a network request and reading from a stream are IO bound operations, you can take advantage of the async api they expose.

if I am using HttpClient.GetAsync (or PostAsync, or PutAsync, etc), is the code really any more asynchronous by also reading the content out asynchronously? If so, how/why?

It sounds a bit weird to say something is "more async". But yes, the former example is more async than the latter.

Lets try to picture the following scenario: You create a library which is responsible for talking to a third party service. You expose a method, lets call it FooAsync, which returns a very large List<Foo>. The user knows the API is async, so he feels good about calling it from the UI thread and not worrying about it blocking for a long duration.

Now, lets see what happeneds when he makes the call with each method:

  1. We execute FooAsync and the http request is made, yielding control to the caller when reaching the first await. Then, continues executing hitting the second await which reads from the recieved stream, again yielding control back to the caller until that finishes. All the while, the UI is free to process messages as both the major operations are done async.

  2. We execute FooAsync and we make the http request. we yield control on the first await, all is good. Then, we receive the response and we read the underlying stream synchronously. While we read (and lets remember that we're reading a very large stream) the UI thread is blocked on the IO operation, unable to process any other messages.

Our second case is definitely undesired and even worse, unexpected. We told the user he's receiving an async api, but we end up blocking for a pretty long amount of time. Not only that, but if Task.Result is called from the UI thread, it may deadlock.

The benefit of using await on both operations is that you create a truely async api, not wasting any time on blocking IO operations and not surprising anyone who calls your method by blocking.

I suggest reading Best Practices in Asynchronous Programming which talks about all those edge cases.

To conclude, if you can go async "all the way", do so.

like image 121
Yuval Itzchakov Avatar answered Nov 03 '22 21:11

Yuval Itzchakov