Alex Rivera | Logout

Why return type of async must be void, Task or Task<T>

Asked 2011-08-10T12:38:29.510
14

I am trying get my hands dirty with async CTP and I noticed that the compiler complains about the async return type. What is the problem with other types?

A simple demo

static void Main(string[] args)
{
    DoWork();
    Console.WriteLine("Returned to main");
    Console.Read();
}

// why do I need to return void, Task or Task<T> here?
// I know I can use something like Task<IEnumerable<string>>
private static async string[] DoWork()
{
    Console.WriteLine("DoWork started");
    return await Task.Factory.StartNew(
        delegate
        {
            Thread.Sleep(2000);                
            Console.WriteLine("DoWork done");
            return new List<string>();
        });        
}
Edit
Report

1 Answer

13

On the await [consumption] side, we are flexible: we can await any type so long as it has the right methods.

On the async method [production] side, we are inflexible: we are hard-coded to return only the Task type (or void). Why the inconsistency?

  1. Iterators already have this behavior...

    An iterator method (one which has a “yield” inside) is hard-coded to return either IEnumerable or IEnumerator. However, you can “foreach” over any type which has GetEnumerator/MoveNext/Current members. So Async is just following suite.

  2. A task is like a future, so it’s good to hard-code it...

    A Task is barely more than a future. A future is a basic fundamental part of a language/platform. There’s no reason for a language two have multiple copies of such a fundamental notion. One is enough. It’s so foundational that you might even add keywords to the language to deal with futures. Anyway, if someone has a future-like thing, or a richer notion of task, then they can build it out of Task or Func. (Our Tasks are already running. If you want to build something that’s “cold”, like F# asyncs or like IObservable, one which doesn’t start until you tell it – then you should build it out of a Func rather than out of a Task).

  3. Further subtleties

    Define this function:

    void f<T>(Func<Task<T>> f)
    

    And invoke it:

    f( () => 1 + await t )
    

    We’d like to be able to infer that T=int in this case. Such inference isn’t possible unless the compiler has hard-coded knowledge that the lambda it passes to “f” has type Task<int>.

Source: Technical intro to the Async CTP

answered 2011-08-10T12:47:44.113

Your Answer