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)":
So how is it that Task.Delay(int)
isn't marked async
(public static async Task Delay(int millisecondsDelay)
)?
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
.