Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Controller: An asynchronous module or handler completed while an asynchronous operation was still pending

I have a very simple ASP.NET MVC 4 controller:

public class HomeController : Controller {     private const string MY_URL = "http://smthing";     private readonly Task<string> task;      public HomeController() { task = DownloadAsync(); }      public ActionResult Index() { return View(); }      private async Task<string> DownloadAsync()     {         using (WebClient myWebClient = new WebClient())             return await myWebClient.DownloadStringTaskAsync(MY_URL)                                     .ConfigureAwait(false);     } } 

When I start the project I see my view and it looks fine, but when I update the page I get the following error:

[InvalidOperationException: An asynchronous module or handler completed while an asynchronous operation was still pending.]

Why does it happen? I made a couple of tests:

  1. If we remove task = DownloadAsync(); from the constructor and put it into the Index method it will work fine without the errors.
  2. If we use another DownloadAsync() body return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return "Give me an error"; }); it will work properly.

Why it is impossible to use the WebClient.DownloadStringTaskAsync method inside a constructor of the controller?

like image 361
dyatchenko Avatar asked Mar 02 '15 08:03

dyatchenko


People also ask

How does async await work in Web API?

The async/await feature solves three performance or scalability problems: They can make your application handle more users. If you have requests that access an external resource such as a database or a web API then async frees up the thread while it is waiting.

What happens when an asynchronous module or handler completed while await?

An asynchronous module or handler completed while an asynchronous operation was still pending while using await and async. Please Sign up or sign in to vote. You will find a very nice answer here on the above link.

What is asynchronous request handling in ASP NET?

Asynchronous request handlers operate differently. When a request comes in, ASP.NET takes one of its thread pool threads and assigns it to that request. This time the request handler will call that external resource asynchronously. This returns the request thread to the thread pool until the call to the external resource returns.

What is async and why should you use it?

These types of apps use async chiefly to keep the UI responsive. For server applications, the primary benefit of async is scalability. The key to the scalability of Node.js is its inherently asynchronous nature; Open Web Interface for .NET (OWIN) was designed from the ground up to be asynchronous; and ASP.NET can also be asynchronous.

Why do I get an invalidoperationexception when performing an asynchronous operation?

When a synchronous handler attempts to perform asynchronous work, you’ll get an InvalidOperationException with the message, “An asynchronous operation cannot be started at this time.” There are two primary causes for this exception. The first is when a Web Forms page has async event handlers, but neglected to set Page.Async to true.


1 Answers

In Async Void, ASP.Net, and Count of Outstanding Operations, Stephan Cleary explains the root of this error:

Historically, ASP.NET has supported clean asynchronous operations since .NET 2.0 via the Event-based Asynchronous Pattern (EAP), in which asynchronous components notify the SynchronizationContext of their starting and completing.

What is happening is that you're firing DownloadAsync inside your class constructor, where inside you await on the async http call. This registers the asynchronous operation with the ASP.NET SynchronizationContext. When your HomeController returns, it sees that it has a pending asynchronous operation which has yet to complete, and that is why it raises an exception.

If we remove task = DownloadAsync(); from the constructor and put it into the Index method it will work fine without the errors.

As I explained above, that's because you no longer have a pending asynchronous operation going on while returning from the controller.

If we use another DownloadAsync() body return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return "Give me an error"; }); it will work properly.

That's because Task.Factory.StartNew does something dangerous in ASP.NET. It doesn't register the tasks execution with ASP.NET. This can lead to edge cases where a pool recycle executes, ignoring your background task completely, causing an abnormal abort. That is why you have to use a mechanism which registers the task, such as HostingEnvironment.QueueBackgroundWorkItem.

That's why it isn't possible to do what you're doing, the way you're doing it. If you really want this to execute in a background thread, in a "fire-and-forget" style, use either HostingEnvironment (if you're on .NET 4.5.2) or BackgroundTaskManager. Note that by doing this, you're using a threadpool thread to do async IO operations, which is redundant and exactly what async IO with async-await attempts to overcome.

like image 63
Yuval Itzchakov Avatar answered Sep 24 '22 06:09

Yuval Itzchakov