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;

    internal ConfiguredTaskAwaitable(Task<TResult> task, bool continueOnCapturedContext)
    {
        this.m_configuredTaskAwaiter = new ConfiguredTaskAwaiter(task, continueOnCapturedContext);
    }

    public ConfiguredTaskAwaiter GetAwaiter()
    {
        return this.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; }

    bool MoveNext();

    void 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);

    bool IsCompleted { get; }

    void 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;

    public FuncAwaitable(Func<TResult> function)
    {
        this.function = function;
    }

    public IAwaiter<TResult> GetAwaiter()
    {
        return new FuncAwaiter<TResult>(this.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;

    public FuncAwaiter(Func<TResult> function)
    {
        this.task = new Task<TResult>(function);
        this.task.Start();
    }

    bool IAwaiter<TResult>.IsCompleted
    {
        get
        {
            return this.task.IsCompleted;
        }
    }

    TResult IAwaiter<TResult>.GetResult()
    {
        return this.task.Result;
    }

    void INotifyCompletion.OnCompleted(Action continuation)
    {
        new Task(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);
        task.Start();
        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);
        task.Start();
        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);
        task.Start();
        return task;
    }

    public static Task<TResult> Run<TResult>(Func<TResult> function)
    {
        // The implementation is similar to:
        Task<TResult> task = new Task<TResult>(function);
        task.Start();
        return 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);

    public static AsyncSubject<TSource> GetAwaiter<TSource>(this IConnectableObservable<TSource> 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();

    // ...
}

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:

0
1
2

Another example:

private static async Task<string> AwaitObservable2()
{
    IObservable<string> observable = new string[]
        {
            "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation",
            "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern",
            "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-3-runtime-context",
        }
        .ToObservable<string>()
        .SelectMany(async url => await new WebClient().DownloadStringTaskAsync(url))
        .Select(StringExtensions.GetTitleFromHtml)
        .Do(Console.WriteLine);

    return await observable;
}

where the GetTitleFromHtml is:

public static string GetTitleFromHtml(this string html)
{
    Match match = new Regex(
        @".*<head>.*<title>(.*)</title>.*</head>.*",
        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>.

58 Comments

  • 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.
    Thx!

  • 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

    hyperlinks?

  • 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.
    Cheers!

  • 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

    job.

  • 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
    interested.

  • 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!

  • Roku Com Link distinct and unique Features
    Roku is one of the most popular media devices due to inexpensive pricing and a wide variety of content coverage. Activation of Roku with a Roku connection is effortless as compared to many other competing gadgets. When you've enabled the Roku com link, the next thing is to enjoy limitless media channels from the online platform.
    Following are some of the smart features of the Roku Com Link for Account Activation:
    New users can easily enable a Roku account using a Roku com connection.
    Thousands of internet services and video sharing platforms are just a few steps away from your screen.
    Con connection setup fee is incredibly inexpensive, and so you can terminate your subscription at any time.
    Roku provides users access to the biggest library with more than 3,000 channels.
    All Roku products and services support a high quality 1080p video content.

  • Roku Com Link distinct and unique Features
    Roku is one of the most popular media devices due to inexpensive pricing and a wide variety of content coverage. Activation of Roku with a Roku connection is effortless as compared to many other competing gadgets. When you've enabled the Roku com link, the next thing is to enjoy limitless media channels from the online platform.
    Following are some of the smart features of the Roku Com Link for Account Activation:
    New users can easily enable a Roku account using a Roku com connection.
    Thousands of internet services and video sharing platforms are just a few steps away from your screen.
    Con connection setup fee is incredibly inexpensive, and so you can terminate your subscription at any time.
    Roku provides users access to the biggest library with more than 3,000 channels.
    All Roku products and services support a high quality 1080p video content.

    https://www.rokucomlink-account.com/

  • Watch your favorite live sports, news, entertainment, and more. Plus, get unlimited access to the entire Roku streaming library To activate Roku code go to URL if you need help to Roku TV activate.If you still face any problem To activate Roku code, then you can reach us at 1-844-412-9807 our experts are available 24*7 to help you out.
    https://www.rokucomlink-account.com/

  • Что случилось? Я новичок в этом, я наткнулся на это, я обнаружил, что это положительно полезно, и это помогло мне во многих случаях. Я надеюсь внести свой вклад и помочь другим клиентам, как это помогло мне. Отличная работа.

    https://www.safetotosite.pro

  • https://ma-study.blogspot.com/

  • It is my first visit to your blog, and I am very impressed with the articles that you serve. Give adequate knowledge for me. Thank you for sharing useful material. I will be back for the more great post. 먹튀검증사이트 But by chance looking at your post solved my problem! I will leave my blog, so when would you like to visit it?!

  • I've been using WordPress on a number of websites for about a year and am worried about switching to another platform. I have heard good things about 토토사이트추천 Is there a way I can transfer all my wordpress content into it? Any help would be really appreciated!

  • Of course, your article is good enough, Keonhacai but I thought it would be much better to see professional photos and videos together. There are articles and photos on these topics on my homepage, so please visit and share your opinions.

  • Your article has answered the question I was wondering about! I would like to write a thesis on this subject, but I would like you to give your opinion once :D <a href="https://google.com.co/url?sa=t&url=https%3A%2F%2Fkeonhacai.wiki">slotsite</a>

  • When I read an article on this topic, <a href="http://maps.google.fr/url?sa=t&url=https%3A%2F%2Fmajorcasino.org">baccaratcommunity</a> the first thought was profound and difficult, and I wondered if others could understand.. My site has a discussion board for articles and photos similar to this topic. Could you please visit me when you have time to discuss this topic?

  • [url=https://casinomalaysia.org]onlinecasinomalaysia[/url]

  • great post

  • thansk sharing

  • Looking forward to seeing more great blog posts here.

  • Your site is so good and informative thanks for sharing this content

  • This article is very good. I like it. It's interesting. Thanks for sharing. Thank you.<a href="https://popmovie888.com/" rel="bookmark" title=" หนังใหม่ชนโรง "> หนังใหม่ชนโรง </a>

  • Great article. I love how the way you write this. It’s super entertaining and very knowledgeable. Thank you!

  • Wow! It's awesome. This is a great deal.

  • Good job on this article! I really like how you presented your facts and how you made it interesting and easy to understand. Thank you.

  • I recently found many useful information in your website especially this blog page. Among the lots of comments on your articles. Thanks for sharing.

  • This is very educational content and written well for a change. It’s nice to see that some people still understand how to write a quality post!

  • Pretty cool work for creating this kind of content. Thanks for sharing.

Add a Comment

As it will appear on the website

Not displayed

Your website