Await is just an Operator

In .NET, and C# in particular, “await” is an operator just like ++ or &&. You can create all sorts of custom awaitable objects and build custom awaitable expressions.

Here is the simplest of examples!

Lets start by defining a task. A task needs to implement a single method:

  • GetAwaiter()
    • Returns an Awaiter. We will get to this next, its also got a schema to implement.
/// <summary>
/// A simple task.
/// </summary>
public class SimpleTask
{
    /// <summary>
    /// Gets the simple awaiter for the task.
    /// </summary>
    /// <returns>The awaiter.</returns>
    public SimpleAwaiter GetAwaiter()
    {
        return new SimpleAwaiter();
    }
}

Now for the Awaiter. The awaiter needs a few things defined:

  • GetResult()
    • Gets the result of the task.
    • Can be a void or non-void method.
  • IsCompleted
    • A property which returns a value indicating whether a task has completed. Ours is synchronous, so this always returns true.
  • ICriticalNotifyCompletion or INotifyCompletion
    • OnCompleted(Action continuation)
      • Called when returning from an async completion. If ICriticalNotifyCompletion is not implemented, then this will also be invoked on a synchronous completion.
    • UnsafeOnCompleted(Action continuation)
      • Only available if ICriticalNotifyCompletion is implemented, and will be invoked on a synchronous completion of the task. OnCompleted will not be called.
using System;
using System.Runtime.CompilerServices;

public class SimpleAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
    /// <summary>
    /// Our task never runs async, so its always completed.
    /// </summary>
    public bool IsCompleted => true;

    /// <summary>
    /// Our task doesnt return anything, so this can be a void method, 
    /// but lets return something so we have something to await.
    /// </summary>
    public string GetResult() => "Hello World";

    public void OnCompleted(Action continuation)
    {
        // OnCompleted is called on returning from an async completion.
        throw new NotImplementedException();
    }

    public void UnsafeOnCompleted(Action continuation)
    {
        // UnsafeOnCompleted is called after a sync completion.
        continuation();
    }
}

Heres an example of how this can all work!

class Program
{
    public static async Task Main(string[] args)
    {
        Console.WriteLine(await new SimpleTask());
    }
}

Give it a shot! The result will be “Hello World” in the console.

A good place to check for more information is the C# language reference: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#await-expressions

Leave a Reply

Your email address will not be published. Required fields are marked *