Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC4 + async/await + return response before action completes

In my MVC4 app I need to add a controller for uploading and processing large files. Immediately after the file is uploaded I need to start async processing of that file and return response to the browser without waiting for the processing to complete.

Obviously I could start a new thread for processing the file manually, but I'm wondering if I can implement this scenario using async/await mechanism introduced with .net 4.5

To test the concept I've tried something like this:

public async Task<ActionResult> Test()
{
    TestAsync();
    return View("Test");
}

public async void TestAsync()
{
    await LongRunning();
}

private Task<int> LongRunning()
{
    return Task<int>.Factory.StartNew(() => Pause());
}

private int Pause()
{
    Thread.Sleep(10000);
    return 3;
}

The async mechanism seems to work in general: when I debug the code I hit the "return View("Test");" line before the line "return 3". However, the browser receives the response only after the Pause method completes.

This seems to behave like regular async controllers (the ones with Async and Completed methods). Is there a way to use async/await in controllers for my scenario?

like image 366
filip Avatar asked Sep 07 '12 14:09

filip


People also ask

What happens when async method is not awaited?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

Does async await start a new thread?

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.

What is the return type of async await?

The behavior of async / await is similar to combining generators and promises. Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.


1 Answers

Obviously I could start a new thread for processing the file manually, but I'm wondering if I can implement this scenario using async/await mechanism introduced with .net 4.5

No, you cannot, because async doesn't change the HTTP protocol.

Svick and James have already posted the correct answers as comments, which I duplicate below for convenience:

IIS can recycle your application pretty much at any time.

If you have long-running things to do async to the request, do them elsewhere. The 'typical' is a persistent queue (MSMQ, Azure, RabbitMQ, etc) with something else (windows service, exe run by task scheduler, app using Quartz.net, etc) processing them.

To summarize, HTTP gives you one request and one response (async - and anything else - won't change this).

ASP.NET is designed around HTTP requests (and makes assumptions like "if there are no outstanding requests, then it's safe to stop this web site"). You can kick off a new thread and keep your upload in memory (which is the easiest way to do it), but it's strongly not recommended.

For your situation, I recommend you follow James' suggestion:

  • When your upload completes, save the upload to persistent storage (e.g., an Azure queue), and return a "ticket" to the browser.
  • Have some other process (e.g., an Azure worker role) process the queue, eventually marking it as complete.
  • Have the browser poll the server with its "ticket". The server uses the "ticket" to look up the file in persistent storage and return whether or not it's complete.

There are some variations to this (e.g., using SignalR to notify the browser when processing is complete), but the general architecture is the same.

This is complex, but it's the right way to do it.

like image 127
Stephen Cleary Avatar answered Oct 03 '22 15:10

Stephen Cleary