24
votes

I'm trying to get the hand of the new async CTP stuff and I'm probably confusing myself here.. I can have this "task method", with no problem:

    public static Task<String> LongTaskAAsync() {
        return Task.Run(() => {
            return("AAA");
            });
        }

But what if I need the task to execute another task, can I mark it as "async" and use "await"? I tried this:

public async static Task<String> LongTaskAAsync() {
        await Task.Delay(2000);
        return Task.Run(() => {
            return("AAA");
            });
        }

But then mysteriously get this compiler error: Since this is an async method, the return expression must be of type 'string' rather than Task<string>

What am I missing here?

2
Try using the return type string instead of Task<string>? (Just a guess.)Ry-
Nah, then it complains that the "return of an async method must be void, Task, or Task<T>" !! which is ironic.. in my example above, the return is Task<String>.. I think that counts ask Task<T>, no?Michael Ray Lovett
okay, I'm beginning to see my own problems here. When the compiler sees "async Task" or "async Task<T>", it creates the task object "for you". In the case of Task<Int32>, for example, it creates a Task<Int32> for you and sets it to the return value of your method (which must be an int). So in my code above, which returns a Task, is not meeting the compilers expectation that I will return a String.Michael Ray Lovett
I'm not sure yet, but I think the answer to my original question of whether or not a task method (ie one that returns a Task object which (usually) represents a hot running async method) can also be an async method, I'm guessing the answer is no. I'm guessing the pattern expects that the methods that really launch async code (represented via a Task object that the developer caused to be created) is supposed to be just doing that asynch task.. and to string together multiple async tasks is achieved by usage of methods marked "async".Michael Ray Lovett
I think I know what you're getting at; see my updated answer.Stephen Cleary

2 Answers

20
votes

You may want to read my async/await intro post.

Return values from async methods are wrapped in a Task<TResult>. Likewise, await unwraps those return values:

public static async Task<String> LongTaskAAsync() {
  await Task.Delay(2000);
  return await Task.Run(() => {
    return("AAA");
  });
}

The reasoning behind this is described in my Async "Why Do the Keywords Work That Way" Unofficial FAQ.

P.S. You can also use Task.FromResult for simple tests like this.

Edit: If you want to create and return the Task object itself, then the method should not be async. One somewhat common pattern is to have a public non-async method that calls the async portion only if necessary.

For example, some kind of asynchronous cache - if the object is in the cache, then return it immediately; otherwise, asynchronously create it, add it to the cache, and return it (this is example code - not thread-safe):

public static Task<MyClass> GetAsync(int key)
{
  if (cache.Contains(key))
    return Task.FromResult(cache[key]);
  return CreateAndAddAsync(key);
}

private static async Task<MyClass> CreateAndAddAsync(int key)
{
  var result = await CreateAsync(key);
  cache.Add(key, result);
  return result;
}
3
votes

Can a “task method” also be an “async” method?

Yes it can be, by simply changing the method signature to public async static Task<Task<String>> LongTaskAAsync() since that is, what it will return.

If you use the async keyword, the runtime will wrap the type you return into a task, to enable asynchronousness. Say if you return a string, the runtime will wrap that into a Task<string>. int will go Task<int> and Task<string> will go Task<Task<string>>. See this console app to clearify:

public class Program
{
    public static void Main(string[] args)
    {
        // start the main procedure asynchron
        Task.Run(() => DoIt()).Wait();
    }

    // for async support since the static main method can't be async
    public static async void DoIt()
    {
        Program p = new Program();

        // use the methods
        string s = await p.GetString();
        int i = await p.GetInt();
        Task<string> tsk = await p.GetTaskOfString();

        // just to prove the task works:

        // C# 5
        string resultFromReturnedTask = await tsk;

        // C# 4
        string resultFromReturnedTask2 = tsk.Result;
    }

    public async Task<string> GetString()
    {
        return "string";
    }

    public async Task<int> GetInt()
    {
        return 6;
    }

    public async Task<Task<string>> GetTaskOfString()
    {
        return Task.Run(() => "string");
    }
}