Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I await something if there's no GetAwaiter method?

I saw some articles about designing a custom awaitable type:

http://books.google.com.br/books?id=1On1glEbTfIC&pg=PA83&lpg=PA83&dq=create+a+custom+awaitable+type

Now consider the example below:

<Button x:Name="BtnA"
        Width="75"
        Margin="171,128,0,0"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        Click="BtnA_Click"
        Content="Button A" />
<Button x:Name="BtnB"
        Width="75"
        Margin="273,128,0,0"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        Content="Button B"  Click="BtnB_OnClick" />

and:

private async void BtnA_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Awaiting Button B..");

    var sx = Observable.FromEvent<MouseButtonEventHandler, 
                                  MouseButtonEventArgs>(a => (b, c) => a(c),
                                  add => BtnB.PreviewMouseDown += add,
                                  rem => BtnB.PreviewMouseDown -= rem)
       .Do(a => a.Handled = true)
       .Take(1);

    await sx;

    MessageBox.Show("Button B Pressed after Button A");
}

private void BtnB_OnClick(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Button B Pressed Without Click in Button A");
}

Why can I await IObservable<T> (in this case when subscription is completed), if there's no GetAwaiter() method?

Is it implemented by the compiler? Is it possible to implement something that can await without to explicit this method (some scenarios which interfaces is being used)? And why isn't there something like:

public interface ITask<out T>
{
    IAwaiter<T> GetAwaiter();
}

public interface IAwaiter<out T> : ICriticalNotifyCompletion
{
    bool IsCompleted { get; }
    T GetResult();
}

... or real interfaces to create a custom awaitable?

like image 559
J. Lennon Avatar asked Sep 25 '14 19:09

J. Lennon


2 Answers

First of all, no.

You can't await something that doesn't have a GetAwaiter method that returns something that has GetResult, OnCompleted, IsCompleted and implements INotifyCompletion.

So, how are you able to await an IObservable<T>?

The compiler, on top of instance methods, also accepts GetAwaiter extensions methods. In this case Reactive Extensions's Observable provides that extension:

public static AsyncSubject<TSource> GetAwaiter<TSource>(this IObservable<TSource> source);

For example, this is how we can make strings awaitable (which compiles but obviously won't actually work):

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

await "bar";
like image 129
i3arnon Avatar answered Oct 21 '22 23:10

i3arnon


You can supply your own implementation of GetAwaiter via an extension method if one does not exist.

http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx

like image 2
Mike Barry Avatar answered Oct 22 '22 01:10

Mike Barry