Understanding C# async / await (2) The Awaitable-Awaiter Pattern

Understanding C# async / await:

What is awaitable

Part 1 shows that any Task is awaitable. Actually there are other awaitable types. Here is an example:

Task<int> task = new Task<int>(() => 0);
int result = await task.ConfigureAwait(false); // Returns a ConfiguredTaskAwaitable<TResult>.

The returned ConfiguredTaskAwaitable<TResult> struct is awaitable. And it is not Task at all:

public struct ConfiguredTaskAwaitable<TResult>
    private readonly ConfiguredTaskAwaiter m_configuredTaskAwaiter;
</span><span style="color: blue;">internal </span><span style="color: black;">ConfiguredTaskAwaitable(</span><span style="color: #2b91af;">Task</span><span style="color: black;">&lt;TResult&gt; task, </span><span style="color: blue;">bool </span><span style="color: black;">continueOnCapturedContext)
    </span><span style="color: blue;">this</span><span style="color: black;">.m_configuredTaskAwaiter = </span><span style="color: blue;">new </span><span style="color: #2b91af;">ConfiguredTaskAwaiter</span><span style="color: black;">(task, continueOnCapturedContext);

</span><span style="color: blue;">public </span><span style="color: #2b91af;">ConfiguredTaskAwaiter </span><span style="color: black;">GetAwaiter()
    </span><span style="color: blue;">return this</span><span style="color: black;">.m_configuredTaskAwaiter;


It has one GetAwaiter() method. Actually in part 1 we have seen that Task has GetAwaiter() method too:

public class Task
    public TaskAwaiter GetAwaiter()
        return new TaskAwaiter(this);

public class Task<TResult> : Task { public new TaskAwaiter<TResult> GetAwaiter() { return new TaskAwaiter<TResult>(this); } }

Task.Yield() is another example:

await Task.Yield(); // Returns a YieldAwaitable.

The returned YieldAwaitable is not Task either:

public struct YieldAwaitable
    public YieldAwaiter GetAwaiter()
        return default(YieldAwaiter);

Again, it just has one GetAwaiter() method. This article will look at what is awaitable.

The awaitable-awaiter pattern

By observing different awaitable / awaiter types, we can tell that an object is awaitable if

  • It has a GetAwaiter() method (instance method or extension method);
  • Its GetAwaiter() method returns an awaiter. An object is an awaiter if:
    • It implements INotifyCompletion or ICriticalNotifyCompletion interface;
    • It has an IsCompleted, which has a getter and returns a Boolean;
    • it has a GetResult() method, which returns void, or a result.

So apparently this awaitable-awaiter pattern is very similar to the iteratable-iterator pattern. Here is the interface definitions of iteratable / iterator:

public interface IEnumerable
    IEnumerator GetEnumerator();

public interface IEnumerator { object Current { get; }

</span><span style="color: blue;">bool </span><span style="color: black;">MoveNext();

</span><span style="color: blue;">void </span><span style="color: black;">Reset();


public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); }

public interface IEnumerator<out T> : IDisposable, IEnumerator { T Current { get; } }

In case the out keyword does not sound familiar, please find detailed explanation in another article Understanding C# Covariance And Contravariance (2) Interfaces.

The “missing” IAwaitable / IAwaiter interfaces

Similar to IEnumerable and IEnumerator interfaces, awaitable / awaiter can be visualized by IAwaitable / IAwaiter interfaces too. This is the non-generic version:

public interface IAwaitable
    IAwaiter GetAwaiter();

public interface IAwaiter : INotifyCompletion // or ICriticalNotifyCompletion { // INotifyCompletion has one method: void OnCompleted(Action continuation);

// ICriticalNotifyCompletion implements INotifyCompletion,
// also has this method: void UnsafeOnCompleted(Action continuation);

</span><span style="color: blue;">bool </span><span style="color: black;">IsCompleted { </span><span style="color: blue;">get</span><span style="color: black;">; }

</span><span style="color: blue;">void </span><span style="color: black;">GetResult();


Please notice GetResult() returns void here. Task.GetAwaiter() / TaskAwaiter.GetResult() is of such case.

And here comes the generic version:

public interface IAwaitable<out TResult>
    IAwaiter<TResult> GetAwaiter();

public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion { bool IsCompleted { get; }

TResult GetResult();


Here the only difference is, GetResult() return a result. Task<TResult>.GetAwaiter() / TaskAwaiter<TResult>.GetResult() is of this case.

Please notice .NET core does not define these IAwaitable / IAwaiter interfaces at all. IAwaitable interface will constraint GetAwaiter() to be instance method. Actually C# supports both GetAwaiter() instance method and GetAwaiter() extension method.

Here these interfaces are used only for better visualizing what is awaitable / awaiter. Now, if looking at above ConfiguredTaskAwaitable / ConfiguredTaskAwaiter, YieldAwaitable / YieldAwaiter, Task / TaskAwaiter pairs again, they all “implicitly” implement these “missing” IAwaitable / IAwaiter interfaces. The rest part of this article will show how to implement awaitable / awaiter.

Await any function / action

In C# await cannot be used with lambda. This code:

int result = await (() => 0);

will cause a compiler error:

Cannot await 'lambda expression'

This is easy to understand because this lambda expression (() => 0) may be a function or a expression tree. Obviously we mean function here, and we can tell compiler in this way:

int result = await new Func<int>(() => 0);

It causes an different error:

Cannot await 'System.Func<int>'

OK, now the compiler is complaining the type instead of syntax. With the understanding of the awaitable / awaiter pattern, Func<TResult> type can be easily made into awaitable.

GetAwaiter() instance method, using IAwaitable and IAwaiter interfaces

First, similar to above ConfiguredTaskAwaitable<TResult>, a FuncAwaitable<TResult> can be implemented to wrap Func<TResult>:

internal struct FuncAwaitable<TResult> : IAwaitable<TResult>
    private readonly Func<TResult> function;
</span><span style="color: blue;">public </span><span style="color: black;">FuncAwaitable(</span><span style="color: #2b91af;">Func</span><span style="color: black;">&lt;TResult&gt; function)
    </span><span style="color: blue;">this</span><span style="color: black;">.function = function;

</span><span style="color: blue;">public </span><span style="color: #2b91af;">IAwaiter</span><span style="color: black;">&lt;TResult&gt; GetAwaiter()
    </span><span style="color: blue;">return new </span><span style="color: #2b91af;">FuncAwaiter</span><span style="color: black;">&lt;TResult&gt;(</span><span style="color: blue;">this</span><span style="color: black;">.function);


FuncAwaitable<TResult> wrapper is used to implement IAwaitable<TResult>, so it has one instance method, GetAwaiter(), which returns a IAwaiter<TResult>, which wraps that Func<TResult> too. FuncAwaiter<TResult> is used to implement IAwaiter<TResult>:

public struct FuncAwaiter<TResult> : IAwaiter<TResult>
    private readonly Task<TResult> task;
</span><span style="color: blue;">public </span><span style="color: black;">FuncAwaiter(</span><span style="color: #2b91af;">Func</span><span style="color: black;">&lt;TResult&gt; function)
    </span><span style="color: blue;">this</span><span style="color: black;">.task = </span><span style="color: blue;">new </span><span style="color: #2b91af;">Task</span><span style="color: black;">&lt;TResult&gt;(function);
    </span><span style="color: blue;">this</span><span style="color: black;">.task.Start();

</span><span style="color: blue;">bool </span><span style="color: #2b91af;">IAwaiter</span><span style="color: black;">&lt;TResult&gt;.IsCompleted
    </span><span style="color: blue;">get
    </span><span style="color: black;">{
        </span><span style="color: blue;">return this</span><span style="color: black;">.task.IsCompleted;

TResult </span><span style="color: #2b91af;">IAwaiter</span><span style="color: black;">&lt;TResult&gt;.GetResult()
    </span><span style="color: blue;">return this</span><span style="color: black;">.task.Result;

</span><span style="color: blue;">void </span><span style="color: #2b91af;">INotifyCompletion</span><span style="color: black;">.OnCompleted(</span><span style="color: #2b91af;">Action </span><span style="color: black;">continuation)
    </span><span style="color: blue;">new </span><span style="color: #2b91af;">Task</span><span style="color: black;">(continuation).Start();


Now a function can be awaited in this way:

int result = await new FuncAwaitable<int>(() => 0);

GetAwaiter() extension method, without IAwaitable interfaces

As IAwaitable shows, all that an awaitable needs is just a GetAwaiter() method. In above code, FuncAwaitable<TResult> is created as a wrapper of Func<TResult> and implements IAwaitable<TResult>, so that there is a  GetAwaiter() instance method. If a GetAwaiter() extension method  can be defined for Func<TResult>, then FuncAwaitable<TResult> is no longer needed:

public static class FuncExtensions
    public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function)
        return new FuncAwaiter<TResult>(function);

So a Func<TResult> function can be directly awaited:

int result = await new Func<int>(() => 0);

Use the built-in awaitable and awaiter: Task and TaskAwaiter

Remember the most frequently used awaitable / awaiter - Task / TaskAwaiter. With Task / TaskAwaiter, FuncAwaitable / FuncAwaiter are no longer needed:

public static class FuncExtensions
    public static TaskAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function)
        Task<TResult> task = new Task<TResult>(function);
        return task.GetAwaiter(); // Returns a TaskAwaiter<TResult>.

Similarly, with this extension method:

public static class ActionExtensions
    public static TaskAwaiter GetAwaiter(this Action action)
        Task task = new Task(action);
        return task.GetAwaiter(); // Returns a TaskAwaiter.

an action can be awaited as well:

await new Action(() => { });

Now any function / action can be awaited:

await new Action(() => HelperMethods.IO()); // or: await new Action(HelperMethods.IO);

If function / action has parameter(s), closure can be used:

int arg0 = 0;
int arg1 = 1;
int result = await new Action(() => HelperMethods.IO(arg0, arg1));

Use Task.Run()

The above code is used to demonstrate how awaitable / awaiter can be implemented. As it is common scenario to await a function / action, .NET provides a built-in API: Task.Run(). Their implementations are similar to:

public class Task
    public static Task Run(Action action)
        // The implementation is similar to:
        Task task = new Task(action);
        return task;
</span><span style="color: blue;">public static </span><span style="color: #2b91af;">Task</span><span style="color: black;">&lt;TResult&gt; Run&lt;TResult&gt;(</span><span style="color: #2b91af;">Func</span><span style="color: black;">&lt;TResult&gt; function)
    </span><span style="color: green;">// The implementation is similar to:
    </span><span style="color: #2b91af;">Task</span><span style="color: black;">&lt;TResult&gt; task = </span><span style="color: blue;">new </span><span style="color: #2b91af;">Task</span><span style="color: black;">&lt;TResult&gt;(function);
    </span><span style="color: blue;">return </span><span style="color: black;">task;


In reality, this is how to await a function:

int result = await Task.Run(() => HelperMethods.IO(arg0, arg1));

and await a action:

await Task.Run(HelperMethods.IO);

Await IObservable<T>

IObservable<T> and IConnectableObservable<T> become awaitable too, if a reference is added for System.Reactive.Linq.dll, a part of Rx (Reactive Extensions). In this library, the GetAwaiter() extension methods are provided:

public static class Observable
    public static AsyncSubject<TSource> GetAwaiter<TSource>(this IObservable<TSource> source);
</span><span style="background: #f5f5f5; color: blue;">public static </span><span style="background: #f5f5f5; color: #2b91af;">AsyncSubject</span><span style="background: #f5f5f5; color: black;">&lt;TSource&gt; GetAwaiter&lt;TSource&gt;(</span><span style="background: #f5f5f5; color: blue;">this </span><span style="background: #f5f5f5; color: #2b91af;">IConnectableObservable</span><span style="background: #f5f5f5; color: black;">&lt;TSource&gt; source);


Each method returns a AsyncSubject<T>, which is an awaiter:

public sealed class AsyncSubject<T> : INotifyCompletion, ISubject<T>, ISubject<T, T>, IObserver<T>, IObservable<T>, IDisposable
    public bool IsCompleted { get; }
    public void OnCompleted();
</span><span style="background: #f5f5f5; color: green;">// ...


So that can be used with the await keyword. Take IObservable<T> as example:

private static async Task AwaitObservable1()
    IObservable<int> observable = Observable.Range(0, 3).Do(Console.WriteLine);
    await observable;

This outputs:


Another example:

private static async Task<string> AwaitObservable2()
    IObservable<string> observable = new string[]
        .SelectMany(async url => await new WebClient().DownloadStringTaskAsync(url))
</span><span style="background: #f5f5f5; color: blue;">return await </span><span style="background: #f5f5f5; color: black;">observable;


where the GetTitleFromHtml is:

public static string GetTitleFromHtml(this string html)
    Match match = new Regex(
        RegexOptions.IgnoreCase | RegexOptions.Singleline).Match(html);
    return match.Success ? match.Groups[1].Value : null;

Executing above AwaitObservable2 method will output the title of each page:

Dixin&#39;s Blog - Understanding C# async / await (1) Compilation
Dixin&#39;s Blog - Understanding C# async / await (3) Runtime Context
Dixin&#39;s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern

which is exactly what’s between <tile> and </title>.


  • FYI: Those weird comments you are getting are mostly spammers trying to get links to their sites embedded in your blog so they get higher SEO scores.

  • whoah this blog is fantastic i love reading your articles.
    Keep up the

    good work! You know, lots of people are looking around for this

    information, you could help them greatly.

  • I am continuously searching online for posts that can assist me.

  • I’d have to test with you here. Which is not something I usually do!
    I get pleasure from reading a put up that can make
    people think. Also, thanks for

    allowing me to remark!

  • certainly like your web site but you have to check the

    spelling on quite a few of your posts. Several of them are rife with
    spelling issues

    and I find it very bothersome to tell the truth nevertheless I’ll certainly come

    back again.

  • When I initially commented I clicked the -Notify me
    when new comments are added-

    checkbox and now every time a remark is added I get 4 emails with the identical comment.

    Is there any method you may take away me

    from that service? Thanks!

  • You must participate in a contest for

    probably the greatest blogs on the web. I will

    recommend this web site!

  • Hi there, i read your blog occasionally and i own a similar one and i was just

    curious if you get a lot of spam responses?
    If so how do you prevent it, any plugin or anything you
    can recommend? I get so much lately it's driving me crazy so any assistance is very much appreciated.

  • Somebody essentially assist to make

    significantly posts I'd state. That is the first time I

    frequented your web page and thus far? I amazed with the

    research you made to create this actual submit

    amazing. Wonderful task!

  • Would you be involved in exchanging


  • Hi there, just became aware of your blog through Google, and found that it is truly

    informative. I’m gonna watch out for brussels.

    I’ll be grateful if you continue

    this in future. Numerous people will be benefited from your writing.

  • Hi there, simply turned into alert to your blog

    through Google, and located that it's truly informative. I am going to

    watch out for brussels. I’ll appreciate in the event you proceed this in future. Many other people shall be benefited from your writing. Cheers!

  • Hmm is anyone else experiencing problems with the pictures on this blog loading?
    I'm trying

    to determine if its a problem on my end or if it's the blog.
    Any feed-back would be greatly appreciated.

  • I am extremely impressed with your writing talents as smartly as with the format in your

    blog. Is this a paid subject matter or did you modify it

    your self? Either way stay up the excellent high quality writing, it’s

    rare to peer a great blog like this one these days..

  • A person necessarily assist to make severely articles I might state.
    That is the very first time I

    frequented your website page and up to now?
    I amazed with the

    research you made to create this actual publish extraordinary.
    Great task!

  • What’s Taking place i'm new to this, I stumbled upon this I have found It

    absolutely helpful and it has aided me out loads. I'm hoping to contribute & aid
    different users like its helped me. Great


  • I comment whenever I especially enjoy a article on a site or
    I have something to contribute to the conversation. Usually it's caused by the passion communicated in the article I looked at. And after this article Understanding C# async / await (2) Awaitable / Awaiter Pattern - Dixin's
    Blog. I was actually moved enough to drop a thought :-P I actually do
    have some questions for you if you usually do not mind.

    Could it be just me or does it give the impression like some of these remarks appear like they
    are left by brain dead visitors? :-P And, if you are writing at additional places,
    I'd like to keep up with anything new you have to post. Could you make a list every one of your shared pages like your linkedin profile, Facebook page or twitter feed?

  • I’ll immediately take hold of your rss feed as I

    can't to find your e-mail subscription hyperlink or e-newsletter service. Do

    you've any? Kindly permit me recognize in

    order that I may subscribe. Thanks.

  • Do you have a spam issue on this site; I also am a blogger,
    and I was curious about your situation; we have created some nice procedures and we

    are looking to exchange solutions with other folks, please shoot me an email if

  • It's really a great and helpful piece of information. I'm happy that you
    simply shared this helpful info with us. Please keep us informed like this.
    Thanks for sharing.

  • Its such as you learn my mind! You seem to understand a lot about
    this, such as you wrote the ebook in it or something.
    I feel that you simply could do with a few p.c.
    to force the message house a bit, however instead of that,
    this is excellent blog. A great read. I will
    definitely be back.

  • This is my first time pay a visit at here and i am really impressed to
    read everthing at alone place.

  • This is really interesting, You're a very skilled blogger. I've joined your rss feed and look forward to seeking more of your excellent post.
    Also, I've shared your site in my social networks!

  • Magnificent goods from you, man. I have understand
    your stuff previous to and

    you are just extremely excellent. I really like what

    you've acquired here, certainly like what you're
    saying and the way in which you

    say it. You make it entertaining and you still care
    for to keep it smart. I

    can not wait to read far more from you. This is really a

    tremendous website.

  • Magnificent beat ! I would like to apprentice while you amend your site, how could i subscribe for
    a weblog website? The account helped me a appropriate deal.
    I had been tiny bit acquainted of this your broadcast offered vibrant clear concept

  • I've been surfing on-line more than 3 hours nowadays, yet I never discovered any attention-grabbing article like yours. It is lovely worth sufficient for me. In my opinion, if all site owners and bloggers made just right content as you probably did, the internet shall be much more helpful than ever before.

  • Hi to every one, it's really a pleasant for me to visit this website, it includes precious Information.

  • Great write-up, I’m regular visitor of one’s blog, maintain up the

    excellent operate, and It is going to be a regular visitor for a lengthy time.

  • L5UKAC Great blog article.Much thanks again. Awesome.

  • I'm not sure why but this website is loading extremely slow for me.
    Is anyone else having this issue or is it a problem on
    my end? I'll check back later on and see if the problem still exists.

  • AKOPLg Major thanks for the blog article.Really looking forward to read more. Cool.

  • utufqh Say, you got a nice blog. Keep writing.

  • Wow, thanks so much for the explanation. You explain things better than Microsoft themselves. I never thought I could understand these async stuffs so deeply. Thanks again, keep up good work.

  • Nice Post for Guest post sites…

    I am seo expert and always focus on guest blogging to drive the relevant traffic to my blog..

  • How kind you are to help me.

  • wonderful put up, very informative. I wonder
    why the opposite specialists of this sector don’t understand this.
    You must continue your writing. I am sure, you’ve a great readers’ base already!

Add a Comment

As it will appear on the website

Not displayed

Your website