OdeToCode IC Logo

Parallel Work in Async MVC Actions

Wednesday, June 20, 2012

One of the samples in the ASP.NET MVC 4 release notes is an example of using an async controller action.

public async Task<ActionResult> Index(string city)
{
var newsService = new NewsService();
var sportsService = new SportsService();

return View("Common",
new PortalViewModel
{
NewsHeadlines = await newsService.GetHeadlinesAsync(),
SportsScores = await sportsService.GetScoresAsync()
});
}

At first glance, it might seem like getting the headlines and getting the sports scores are two operations that happen in parallel, but the way the code is structured this can’t happen. It’s easier to see if you add some async methods to the controller and watch the different threads at work in the debugger.

public async Task<ActionResult> Index(string city)
{
return View("Common",
new PortalViewModel
{
NewsHeadlines = await GetHeadlinesAsync(),
SportsScores = await GetScoresAsync()
});
}
async Task<IEnumerable<Score>> GetScoresAsync()
{
await Task.Delay(3000);
// return some scores ...
}

async Task<IEnumerable<Headline>> GetHeadlinesAsync()
{
await Task.Delay(3000);
// return some news
}

In the methods I’ve introduced a delay using Task.Delay (which returns a Task you can await and thereby free the calling thread, unlike Thread.Sleep which will block). The total time to render the view will be at least 6,000 milliseconds, because the Index action awaits the result of GetHeadlinesAsync. Awating will suspend execution before GetScoresAsync has a chance to start.
If you want the headlines and scores to work in parallel, you can kick off both async calls before awaiting the first result.
public async Task<ActionResult> Index(string city)
{
var newsService = new NewsService();
var sportsService = new SportsService();

var newsTask = newsService.GetHeadlinesAsync();
var sportsTask = sportsService.GetScoresAsync();

return View("Common",
new PortalViewModel
{

NewsHeadlines = await newsTask,
SportsScores = await sportsTask
});
}