I have an ASP.NET MVC async action method which looks like this:
public async Task<ActionResult> IndexAsync()
{
var tasks = new List<Task<string>>
{
GetSomethingAsync("a"),
GetSomethingAsync("b")
};
await Task.WhenAll(tasks);
return View();
}
private async Task<string> GetSomethingAsync()
{
var data = await _someService.GetSomethingAsync().ConfigureAwait(false);
return data.SomeData;
}
Now, when i debug and step over the tasks
variable creation, the tasks are executed immediately. In other words, when i hover over tasks
in the await
line, they say "RanToCompletion".
Why?
From my understanding, the tasks should be created, but be in the "WaitingForActivation" state until triggered by the await Task.WhenAll(tasks)
blocking call.
Can someone explain to me what's going on? I've written code like this before and it normally works as expected, so i'm wondering if this is an ASP.NET or ASP.NET MVC async controller thing?
TIA.
EDIT If i change the code to:
var tasks = new List<Task<string>>
{
Task.Run(() => GetSomethingAsync("a")),
Task.Run(() => GetSomethingAsync("b"))
};
The method runs as expected (tasks not executed until await
).
I've generally never needed to do this before when running async tasks, is this needed in ASP.NET MVC ?
Per your comment you actually did not have any real asynchronous code - so indeed task will return synchronously and be in completed state.
The easiest way to make method true async is await Task.Yield()
. This is fine for unit testing or methods that have to be async for some reason but don't consume much time. If you need to run slow (blocking or just CPU-intensive) methods - Task.Run
as you have in the question is a reasonable way to make task running on separate thread.
Notes
async
does not by itself make it asynchronous nor await
creating any threads by itself.await
'ing method will not be able to find thread to run on.ConfigureAwait(false)
does not prevent load-based deadlocks in ASP.Net and conveniently looses HttpContext.Current
and thread's CultureInfo
- be careful to use it in ASP.Net especially that there is basically no upside to do so (cost of restoring context on new thread is very low compared to WPF/WinForm cross-thread invocation).