Reactive Extensions–FromAsync

Reactive Programming

I’ve been struggling with getting my head around the exact relationship between Rx’s approach to asynchronous programming and the traditional Begin/End pattern common in .NET programming.

With the help of Paul Betts I created a side by side example, starting with the traditional approach and then migrating it to Rx using Observable.FromAsyncPattern This, I think, makes the relationship much clearer.

We decided to use the Bing Translator service (again), but this time to start with the traditional approach.  We did this in WPF though we certainly could have done the exact same thing in Silverlight or Windows Phone.

We started by creating the world’s simplest (and ugliest) UI, consisting of a TextBox to put in text we want to translate, a button to start the translation, and a TextBlock to display the output.

 <Grid>
     <Grid.RowDefinitions>
         <RowDefinition
             Height="50*" />
         <RowDefinition
             Height="52*" />
         <RowDefinition
             Height="209*" />
     </Grid.RowDefinitions>
     <TextBox
         Height="23"
         HorizontalAlignment="Left"
         Margin="68,19,0,0"
         Name="Input"
         VerticalAlignment="Top"
         Width="260" />
     <Button
         Content="Translate"
         Grid.Row="1"
         Height="23"
         HorizontalAlignment="Left"
         Margin="68,17,0,0"
         Name="Translate"
         VerticalAlignment="Top"
         Width="252"
         Click="Translate_Click" />
     <TextBlock
         Grid.Row="2"
         Height="23"
         HorizontalAlignment="Left"
         Margin="72,27,0,0"
         Name="Output"
         Text="Ready..."
         VerticalAlignment="Top"
         Width="248" />
 </Grid>

Next, we added the Bing Translation Service to our application by adding a Service Reference to http://api.microsofttranslator.com/V2/Soap.svc (be sure to click on Advanced and to check the Generate Asynchronous Operations checkbox to ensure that you get support for Begin/End Translate).

The next step is to get a Bing API key, which we placed in MainWindow.xaml.cs as a constant,

const string AppId = "8E94xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxB2";

To translate the text, you begin by “newing up” a LanguageServiceClient, and then calling BeginTranslate on that client, passing in six strings and the delegate for your asynchronous callback, and the LanguageServiceClient as the async State.

The six strings are the AppId you just created, the text you want to translate, the language you want to translate from, the language you want to translate to, and then the content type and category, as shown here (we are translating from English to Spanish):

private void Translate_Click(
    object sender, RoutedEventArgs e )
{
    var client = new LanguageServiceClient();
    client.BeginTranslate(
        AppId,
        Input.Text,
        "en-US",
        "es-ES",
        "text/plain",
        "general",
        new AsyncCallback(TranslationCompleted),
        client );
}

When you run this code, the input text is passed to the translation service asynchronously, and when the service has completed the translation, your callback method is invoked.

The callback method extracts the LanguageServiceClient and then uses that to call EndTranslate (passing in the IAsyncResult object).  What it gets back is the translated string, which we can display by calling Dispatcher.BeginInvoke to put the writing of the text back onto the proper thread,

private void TranslationCompleted(
    IAsyncResult result )
{
    var client =
        result.AsyncState as LanguageServiceClient;
    string translated =
        client.EndTranslate( result );
    Dispatcher.BeginInvoke(
        new Action(() => Output.Text = translated ));

}

Translating to Rx

The purpose of that exercise was to see how this translates directly to Rx.  To do so, we created a second application, used the exact same Xaml, added the same service reference to Bing, and even added the same API string.

We then created a single method for the translation. We begin by again creating an instance of LanguageServiceClient, but this time instead of calling the asynchronous BeginTranslate method, we called Observable.FromAsyncPattern.  The types we passed in were seven strings (the six strings used as parameters in the non-async version of the call plus a string for the return type) and as parameters we passed in the two asynchronous call delegates.  The return of calling FromAsyncPattern was a func that takes six strings as parameters but returns an IObservable.

We invoke that func and it returns an IObservable of string which we then Subscribe to on the Dispatcher thread, passing in a lambda expression to put the text observed into the output box,

private void Translate_Click( object sender, RoutedEventArgs e )
{
    var client = new LanguageServiceClient();

    var translateFunc = Observable.FromAsyncPattern<
        string,
        string,
        string,
        string,
        string,
        string,
        string>(
                  client.BeginTranslate,
                  client.EndTranslate );

    var result = translateFunc(
        AppId,
        Input.Text,
        "en-US",
        "es-ES",
        "text/plain",
        "general" );

    result.ObserveOnDispatcher().Subscribe(
        x => Output.Text = x );
}

Putting these two applications side by side greatly clarifies how Rx translates an asynchronous pattern into a synchronous one that is easier to read and to maintain.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Linq, Mini-Tutorial, Reactive and tagged . Bookmark the permalink.

10 Responses to Reactive Extensions–FromAsync

Comments are closed.