Luke T O'Brien
Luke T O'Brien

Reputation: 2835

ASP.NET Identity UserStore: Why return Task?

I have wondered about this for a while now, all UserStore methods return a Task or a typed Task<T>, but why do they? Why not just return T? I understand that they are Aync methods and are part of OWIN middleware, does that have something to do with it?

I also notice that some methods that return a plain old Task actually return an empty Task. The samples of the asp.net website return Task.FromResult<object>(null) http://www.asp.net/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity. I have also seen Task.FromResult(0), and if I return a Task with a Action:

return new Task(() =>
{
    context.User.Add(new User); context.SaveChanges()
})

Then the Action inside the Task is not invoked. Why is this and what is the point of returning an empty Task? It seems to me that returning a Task is akin to a void method, is that correct?

Upvotes: 2

Views: 1013

Answers (2)

Mohsen Esmailpour
Mohsen Esmailpour

Reputation: 11554

According to MSDN / Asynchronous Programming documentation:

Asynchrony is essential for activities that are potentially blocking, such as when your application accesses the web. Access to a web resource sometimes is slow or delayed. If such an activity is blocked within a synchronous process, the entire application must wait. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes.

Database call (specially in cloud environments like Microsoft Azure and others) is blocking I/O process and for this reason most of the APIs in the ASP.NET Identity system are asynchronous.

And why empty Task ? returning empty Task is a best practice in Async/Await Programming model. There are three possible return types for async methods: Task, Task<T>; and void, but the natural return types for async methods are just Task and Task<T>. When converting from synchronous to asynchronous code, any method returning a type T becomes an async method returning Task<T>, and any method returning void becomes an async method returning Task.

  • Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.
  • Async void methods have different composing semantics. Async methods returning Task or Task<T> can be easily composed using await, Task.WhenAny, Task.WhenAll and so on. Async methods returning void don’t provide an easy way to notify the calling code that they’ve completed. It’s easy to start several async void methods, but it’s not easy to determine when they’ve finished. Async void methods will notify their SynchronizationContext when they start and finish, but a custom SynchronizationContext is a complex solution for regular application code.
  • Async void methods are difficult to test. Because of the differences in error handling and composing, it’s difficult to write unit tests that call async void methods. The MSTest asynchronous testing support only works for async methods returning Task or Task<T>. It’s possible to install a SynchronizationContext that detects when all async void methods have completed and collects any exceptions, but it’s much easier to just make the async void methods return Task instead.

You should prefer async Task to async void. Async Task methods enable easier error-handling, composability and testability. For more information read this article: Best Practices in Asynchronous Programming

Upvotes: 3

pysco68
pysco68

Reputation: 1146

First of all I really would recommend your read MSDN / Asynchronous Programming with Async and Await which is really helpful to get an understanding of HOW all of the async/await works (behind the curtains).

The reason why those techniques are actually used is that the UserManager implementations usually imply working against a database, which is "slow" in Computer time (great reading from Jeff Atwood on that topic: http://blog.codinghorror.com/the-infinite-space-between-words/).

For that reason the UserManager methods give you the promise to provide you with TypeX or ResultY in the future, but it will take time. In order to liberate resources while your application is waiting for the DB to answer (as an example) the waiting thread is put to sleep be returned to the ThreadPool, and that liberated capacity could be used for yet another incomming request (or whatever task that might have completed asynchronously during that time).

Edit: thank you @Neil Hibbert

Upvotes: 5

Related Questions