Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do TaskFactory.FromAsync() overloads require a state object to be supplied?

As detailed in the TPL and Traditional .NET Framework Asynchronous Programming MSDN article and Stephen Toub's Tasks and the APM Pattern blog post, the TaskFactory.FromAsync() and TaskFactory<TResult>.FromAsync() methods can be used to adapt Begin*() and End*() APIs conforming to the APM pattern for use with the Task Parallel Library.

Below I will address only the TaskFactory<TResult>.FromAsync() overloads, as my question does not depend on whether the APM-conforming APIs result in a value.

There are three overloads that accept an IAsyncResult object, namely:

  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>)
  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>, TaskCreationOptions)
  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>, TaskCreationOptions, TaskScheduler)

I understand that these APIs are useful in scenarios where the Begin*() API method takes more than three parameters in addition to the AsyncCallback and state object (e.g. Too many arguments in BeginXXX for FromAsync?), or when the Begin*() API does not fully conform to the APM pattern because it does not take a state object parameter (e.g. How can I use the AsyncCTP with an TFS APM Method (Query.Begin/EndQuery)?).

What I don't understand is why the remaining overloads (e.g. FromAsync(Func<AsyncCallback, Object, IAsyncResult>, Func<IAsyncResult, TResult>, Object)) all require a state object to be supplied. This state object is passed to the Begin*() API, but in the APM pattern, the state object is for private use by the caller. So, it shouldn't matter what state object is passed to the Begin*() API by FromAsync().

The MSDN article linked above has a section titled "Providing Custom State Data" which recommends capturing any required state in a continuation delegate and passing null for the state object to FromAsync(). However, this does not explain why the FromAsync() methods require a state object to be given.

Why is a state object required to be passed when it is not accessible to the returned Task<TResult>?

like image 433
Daniel Trebbien Avatar asked Aug 01 '15 17:08

Daniel Trebbien


1 Answers

Well, If you follow the source code you can see that the state can indeed be accessed from the Task instance using the (not so surprisingly named) Task.AsyncState:

var task = Task.Factory.FromAsync<TResult>(...);
object state = task.AsyncState

This property holds the state of the asynchronous operation in other scenarios as well, like Task.Factory.StartNew:

var task = Task.Factory.StartNew(_ => { }, "bar");
Console.WriteLine(task.AsyncState); // prints "bar"

So, since the state is accessible, it's expected to be able to pass a state in as a parameter and doubling the API once with the state parameter and once without doesn't sound like the better option. Especially when you can just pass in null.

like image 118
i3arnon Avatar answered Sep 23 '22 16:09

i3arnon