Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is Task.Delay awaitable if it's not marked async?

I'm looking at Task.Delay(int) decompiled in ILSpy:

// System.Threading.Tasks.Task
[__DynamicallyInvokable]
public static Task Delay(int millisecondsDelay)
{
    return Task.Delay(millisecondsDelay, default(CancellationToken));
}

This method is used like await Task.Delay(5000);, and the intellisense even says "(awaitable)":

enter image description here

So how is it that Task.Delay(int) isn't marked async (public static async Task Delay(int millisecondsDelay))?

like image 769
rory.ap Avatar asked Jan 30 '15 13:01

rory.ap


People also ask

What happens if async method is not awaited?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

What happens if you don't await a task?

If we don't await any of the tasks, the result will be similar to the previous example with a single task: the exception will be lost with the tasks. However, if we await all the tasks, considering multiple exceptions can be thrown, one for each task, the result for the parent method may not be what we are expecting.

What is difference between task and async await?

async, await, and TaskThe await keyword waits for the async method until it returns a value. So the main application thread stops there until it receives a return value. The Task class represents an asynchronous operation and Task<TResult> generic class represents an operation that can return a value.

Is Task run async?

NET, Task. Run is used to asynchronously execute CPU-bound code.


1 Answers

What's awaitable is the Task Task.Delay returns. Each method returning a Task/Task<TResult> is awaitable. async is just an implementation detail allowing you to use await in that method and the whole state machine it generates.

More generally, every thing that has a GetAwaiter method (extension methods count as well) that return something that has IsCompleted, OnCompleted and GetResult can be awaited.

For example, Task.Yield returns YieldAwaitable which isn't a Task and looks like this:

public struct YieldAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
    public void OnCompleted(Action continuation);
    public void UnsafeOnCompleted(Action continuation);
    public void GetResult();
    public bool IsCompleted { get; }
}

*UnsafeOnCompleted here is just an optimization, await would work without it.

It's important to note that the compiler in this case (same as in other cases like GetEnumerator for foreach) doesn't expect an interface or a base class. It basically uses duck typing (i.e. "if it walks like a duck...") and simply looks for a GetAwaiter method that returns anything (doesn't matter what type or interface or if it's a class or a struct) that has the other 3 members (IsCompleted, OnCompleted and GetResult)

For example, this is how you can make await "bar" compile (it will fail in runtime of course):

public static Awaiter GetAwaiter(this string s)
{
    throw new NotImplementedException();
}
public abstract class Awaiter : INotifyCompletion
{
    public abstract bool IsCompleted { get; }
    public abstract void GetResult();
    public abstract void OnCompleted(Action continuation);
}

In conclusion, you don't need async to return an awaitable and moreover most Task-returning methods in the .Net framework don't use it and explicitly return a Task.

like image 80
i3arnon Avatar answered Sep 21 '22 13:09

i3arnon