Awaitables are the types on which await can be called. It happens due to “duck typing” – the only thing which makes type “awaitable” is an existence of the GetAwaiter() method that returns type implementing INotifyCompletion interface. That’s it.
Obviously, the most popular awaitables are Task and Task<T> so we can await them – this ends up in a possibility to call:
1 |
await DoSomethingAsync(); |
if DoSomethingAsync method returns Task or Task<T>. Another popular awaitables are ValueTask & ValueTask<T> and ConfiguredTaskAwaiter & ConfiguredValueTaskAwaiter (when you use ConfigureAwait).
But nothing stops us to write our own awaitables. Moreover, as I said duck typing is involved here so the type itself does not have to define proper GetAwaiter method. We can use extension method and it will satisfy duck typing.
Thus, we can make ANY type awaitable. So, we can make bool awaitable:
1 2 3 4 |
internal static class BoolExtensions { public static BoolAwaiter GetAwaiter(this bool value) => new BoolAwaiter(value); } |
Awaiter uses yet another set of duck-typed methods to satisfy underlying state machine:
1 2 3 4 5 6 7 8 9 |
internal class BoolAwaiter : INotifyCompletion { private readonly bool _value; public BoolAwaiter(bool value) => _value = value; public bool IsCompleted => true; public void OnCompleted(Action continuation) { } // Never called public bool GetResult() => _value; } |
Because IsCompleted is always true (checked by the underlying async state machine), GetResult will be called for the result. Here OnCompleted is never called because it is a callback called when the operation “completed” (to execute continuation somehow).
So now we can write:
1 2 |
var result = await false; // result will be false var result = await true; // result will be true |
And because GetResult returns bool… which is now awaitable, we can make recurrent calls:
1 2 |
await await true; await await await false; |
And because in the end it is a bool, it has its all normal operators:
1 |
await (await await false && await true); |
Summary
BoolAwaiter presented here is just for an educational purposes to better understand awaitables. And for fun a little. You can write your own, for example making int awaitable 🙂 Obviously, it has no practical usage and may be even dangerous (imagine a DoAsync method returning int instead of Task<int> by mistake).
If you found it interesting, this is just an example of work for a much bigger project – I really do believe it’s going to be the best in the market on-line course about asynchronous and concurrent programming in .NET.