The Magic of using Asynchronous Methods in ASP.NET 4.5 plus an important gotcha
First, I encourage you to listen to episode 327 of the Hanselminutes podcast. We called it "Everything .NET programmers know about Asynchronous Programming is wrong" and I learned a lot. I promise you will too.
Often we'll find ourselves doing three or four things on one page, loading stuff from a number of places. Perhaps you're loading something from disk, calling a web service, and calling a database.
You can do those things in order, synchronously, as is typical. Add up the duration of each Task:
public void Page_Load(object sender, EventArgs e)
{
var clientcontacts = Client.DownloadString("api/contacts");
var clienttemperature = Client.DownloadString("api/temperature");
var clientlocation = Client.DownloadString("api/location");
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(clientcontacts);
var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(clientlocation);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
Location.Text = location;
}
Each of this three calls takes about a second, so the total type is 3 seconds. They happen one after the other.
Intuitively you may want to make these async by marking the public void Page_Load with async and then awaiting three tasks.
However, Page_Load is a page lifecycle event, and it's a void event handler. Damian Edwards from the ASP.NET team says:
Async void event handlers in web forms are only supported on certain events, as you've found, but are really only intended for simplistic tasks. We recommend using PageAsyncTask for any async work of any real complexity.
Levi, also from the ASP.NET team uses even stronger language. Basically, DO NOT use async on void event handlers like this, it's not worth it.
Async events in web applications are inherently strange beasts. Async void is meant for a fire and forget programming model. This works in Windows UI applications since the application sticks around until the OS kills it, so whenever the async callback runs there is guaranteed to be a UI thread that it can interact with. In web applications, this model falls apart since requests are by definition transient. If the async callback happens to run after the request has finished, there is no guarantee that the data structures the callback needs to interact with are still in a good state. Thus why fire and forget (and async void) is inherently a bad idea in web applications.
That said, we do crazy gymnastics to try to make very simple things like Page_Load work, but the code to support this is extremely complicated and not well-tested for anything beyond basic scenarios. So if you need reliability I’d stick with RegisterAsyncTask.
Using async with voids is not stable or reliable. However, all you have to do is call Page.RegisterAyncTask - it's not any trouble and you'll be in a better more flexible place.
public void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
public async Task LoadSomeData()
{
var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
var clientlocation = Client.DownloadStringTaskAsync("api/location");
await Task.WhenAll(clientcontacts, clienttemperature, clientlocation);
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(await clientcontacts);
var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clientlocation);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
Location.Text = location;
}
You can simplify this even more by removing the (in this case totally unnecessary) Task.WhenAll and just awaiting the result of the Tasks one by one. By the time Task.WhenAll has happened here, the tasks are already back. The result is the same. This also has the benefit of reading like synchronous code while giving the benefits of async.
public async Task LoadSomeData()
{
var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
var clientlocation = Client.DownloadStringTaskAsync("api/location");
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(await clientcontacts);
var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clientlocation);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
Location.Text = location;
}
This now takes just a hair over a second, because the three async tasks are happening concurrently. Async stuff like this is most useful (and most obvious) when you have multiple tasks that don't depend on one another.
Do remember to mark the .aspx page as Async="true" like this:
<%@ Page Title="Async" Language="C#" CodeBehind="Async.aspx.cs" Inherits="Whatever" Async="true" %>
Related Links
Sponsor: A huge thank you to my friends at Red Gate for their support of the site this week. Check out Deployment Manager! Easy release management - Deploy your .NET apps, services and SQL Server databases in a single, repeatable process with Red Gate’s Deployment Manager. There’s a free Starter edition, so get started now!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
There are two potential benefits, but first let's clear up some confusion.
Using "await" in ASP.NET does not change the fact that HTTP is a fundamentally synchronous protocol. The client makes the request, and must wait for the results from the server. Whether those results are made up of async calls internally or synchronous ones is irrelevant from the point of view of the client: the call takes as long as it takes to complete. "async" is an implementation detail.
So, why use it? Those two potential benefits are:
1. Parallelization. As you see in the example above, you can parallelize work, and therefore return the data to the client sooner. The client spends less time waiting. However, this isn't necessarily free: if the tasks are I/O bound, then there is great benefit to the parallelization (as it is here); however, if the tasks are compute bound, then you're just spinning up several threads that are now bound to computation that would otherwise be able to service requests. That is, in the compute bound scenario, you're trading scalability (how many clients can you can service) for performance (how quickly you can return the response to a single client).
2. Freeing up otherwise pointlessly waiting threads. In the case of I/O bound operations (whether that's disk I/O or network I/O), the waiting that a thread might normally do is purposeless. Using an async version of the I/O method allows the worker thread -- which are generally in limited supply -- to go back into the worker pool to start servicing another request. When the I/O is ready, and a thread from the worker pool is available, then the original request can be completed. This can have a fairly substantial benefit in scalability of the server, because it means servicing lots more requests with far fewer resources.
Hope this helps shine some light on why "async" is a benefit for ASP.NET apps.
I really needed this. Problem is, the production server is running Windows 2003 server so I am locked at .net 3.5. I cannot use async :-(
My app had to load 18,000 records from a spreadsheet.
My solution was to write a winforms app just to load legacy data since winforms is more tolerant.
If I use the results of the async operation to change the visibility of some controls - e.g. changing the active view in a MultiView control - the wrong scripts have been registered, and my page doesn't work as intended.
Is there any way to resolve this without resorting to an async void handler?
Full details are on this blog post.
So, the blanket statement of "not stable or reliable" is IMO going too far. Of course, async void methods should be avoided, but to be clear, the ASP.NET runtime *does* support them. Also, there are checks in the page lifecycle, so if you use async void at an inappropriate time, ASP.NET will immediately raise an exception.
For myself, I do recommend using RegisterAsyncTask if possible, but don't freak out if you have to fall back on async void. E.g., the major drawbacks of RegisterAsyncTask (as pointed out by @Richard) are that it doesn't start the background tasks running immediately and there's only a single "sync" point where we wait for them all to complete at the same time.
On a side note, your Task.WhenAll recommendation only works well because it's a contrived example (all tasks always take the same amount of time). In real-world code, I'd recommend Task.WhenAll.
Async/await has always been billed as the "avoid UI thread blocking" scenario for IO calls. Does it make sense to run a processor bound task that could take several seconds to complete asynchronously? If so what kind of pitfalls should I expect?
@Ben - take a look at Task.Run; you can spin off a thread for the background work, freeing up the web worker thread while that conversion is processed.
I've heard that async/await should only be used for IO operations (file system, network, web, ect.). Most of the async articles are referencing Windows desktop solutions, which place heavy emphasis on not blocking the UI thread.
I want to understand some of the differences that a web server environment will encounter in comparison to what I'm hearing from the Win App crowd.
Taken from MSDN "The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes".
I thought that's why we had Task.WhenAll so we could await multiple async tasks.
This seems like the page lifecycle was subverted to allow this async stuff to happen.
@Daniel: You should not use Task.Result in async code; it's easy to cause deadlocks.
@Ben: All three tasks are running when you call the first await, so even though that method is suspended, the other tasks are already running.
@MarcelWijnands: It's the difference between "wait for the first task, then continue the method, wait for the second task, then continue the method, wait for the third task, then continue the method" and "wait for all tasks, then continue the method". The Task.WhenAll approach has less switching back and forth.
This was added to the page lifecycle when asynchronous pages were first introduced in .NET 2.0. In .NET 4.5, the PageAsyncTask was made async-aware, but it still uses the same mechanism to execute the asynchronous code before PreRenderComplete.
public void ExecuteRegisteredAsyncTasks()
{
if (_legacyAsyncTaskManager != null && !_legacyAsyncTaskManager.TaskExecutionInProgress)
{
HttpAsyncResult result = _legacyAsyncTaskManager.ExecuteTasks(null, null);
if (result.Error != null)
{
throw new HttpException(null, result.Error);
}
}
}
The _legacyAsyncTaskManager field is assigned in the RegisterAsyncTask method if you're using the legacy synchronization context. As soon as you set targetFramework="4.5" on the httpRuntime element, you're using the new task-friendly synchronization context, and this field won't be assigned.
For example, say at some point in a blog application you're calling out to a database to get the currently logged in user's profile. Would it be better to have your MyBlog.GetCurrentUser do the call asynchronously but return a User object, or return a Task<User> and don't wait on the result until just before it is displayed?
According to the dictionary "synchronous" is "happening, existing, or arising at precisely the same time", e.g. "Synchronized swimming".
Yet everybody says, like Mr. Hanselman here, "You can do those things in order, synchronously". I would think it is called "consecutive", exact opposite of "synchronous"
Anyway, thanks for an informative article.
var clientcontacts = await Client.DownloadStringTaskAsync("api/contacts");
.
.
.
var contacts = JsonConvert.DeserializeObject<List<Contact>>(clientcontacts);
I've a suggestion, you can add 2 simple graphs, one to visualize the async 3 tasks, and the other to visualize the normal 3 tasks, this will give reader the benefit of parallelism like Brad Wilson wrote above.
I have been fiddling around with parallel.foreach, async, httpclient, postasync...the only thing I never understood was, Why didn't Microsoft made Interface for HttpClient? It would be pretty sick using IHttpClient for mocking and make life easier.
Secondly why web api uses datacontract serializer by default? can we not change it to you xml and json by default in web.config or somewhere?
we do have below lines in app_start
configuration.Formatters.Clear();
configuration.Formatters.Add(new XmlMediaTypeFormatter
{
Indent = false,
UseXmlSerializer = true
});
configuration.Formatters.Add(new JsonMediaTypeFormatter());
but when you need to post the data you still need to tell httpclient to use xmlserializer. Thats the only problems I have encountered right now (can be more).
Comments are closed.
To me "async" in the context of ASP.NET means: Responsive in terms of users don't have to wait long until a page is loaded after a postback.
To the ASP.NET team it seems to mean more of a: The users still needs to wait as long as ever, you simply can do several things in parallel on the server.
So until now I see no benefits at all. When would I really want to do multiple parts in parallel on the server? I really don't get it.
When wanting user-centric asnyc behaviour, I would do some Ajax call to the server and would be done.
So instead on describing how "cool" server-side async is, I would love to see real world example usages that justify using server-side asnyc.
Or maybe I'm completely wrong...