How to declare a not started Task that will Await for another Task?

The problem is that you are using the non-generic Task class, that is not meant to produce a result. So when you create the Task instance passing an async delegate:

Task myTask = new Task(async () =>

...the delegate is treated as async void. An async void is not a Task, it cannot be awaited, its exception cannot be handled, and it's a source of thousands of questions made by frustrated programmers here in StackOverflow and elsewhere. The solution is to use the generic Task<TResult> class, because you want to return a result, and the result is another Task. So you have to create a Task<Task>:

Task<Task> myTask = new Task<Task>(async () =>

Now when you Start the outer Task<Task> it will be completed almost instantly because its job is just to create the inner Task. You'll then have to await the inner Task as well. This is how it can be done:

myTask.Start(TaskScheduler.Default);
Task myInnerTask = await myTask;
await myInnerTask;

You have two alternatives. If you don't need an explicit reference to the inner Task then you can just await the outer Task<Task> twice:

await await myTask;

...or you can use the built-in extension method Unwrap that combines the outer and the inner tasks into one:

await myTask.Unwrap();

This unwrapping happens automatically when you use the much more popular Task.Run method that creates hot tasks, so the Unwrap is not used very often nowadays.

In case you decide that your async delegate must return a result, for example a string, then you should declare the myTask variable to be of type Task<Task<string>>.

Note: I don't endorse the use of Task constructors for creating cold tasks. As a practice is generally frowned upon, for reasons I don't really know, but probably because it is used so rarely that it has the potential of catching other unaware users/maintainers/reviewers of the code by surprise.

General advice: Be careful everytime you are supplying an async delegate as an argument to a method. This method should ideally expect a Func<Task> argument (meaning that understands async delegates), or at least a Func<T> argument (meaning that at least the generated Task will not be ignored). In the unfortunate case that this method accepts an Action, your delegate is going to be treated as async void. This is rarely what you want, if ever.


new Task(async () =>

A task does not take a Func<Task>, but an Action. It will call your asynchronous method and expect it to end when it returns. But it does not. It returns a task. That task is not awaited by the new task. For the new task, the job is done once the method returned.

You need to use the task that already exists instead of wrapping it in a new task:

[TestMethod]
public async Task SimpleTest()
{
    bool isOK = false;

    Func<Task> asyncMethod = async () =>
    {
        Console.WriteLine("Task.BeforeDelay");
        await Task.Delay(1000);
        Console.WriteLine("Task.AfterDelay");
        isOK = true;
        Console.WriteLine("Task.Ended");
    };

    Console.WriteLine("Main.BeforeStart");
    Task myTask = asyncMethod();

    Console.WriteLine("Main.AfterStart");

    await myTask;
    Console.WriteLine("Main.AfterAwait");
    Assert.IsTrue(isOK, "OK");
}