Reactive Programming, Posting # 8
One of the tried and true patterns in .NET programming is to call an Asynchronous service (e.g., BeginGetResponse) and to then provide a callback to a second method for when the call completes. This can get very complex very quickly if you have chained calls (call this, then when you finish, call that)
Let’s take a look at how you can simplify such a call with Rx by examining making a HTTPWebRequest.
We’ll begin by creating a dead-simple UI, consisting of a TextBox (to type in a URL), a TextBlock (to display the returned HTML) and a button (to kick things off). For simplicity, we* created this as a WPF application, though you could certainly do the same in Silverlight or Windows Phone,
* This example was worked through with Paul Betts who has forgotten more about Rx than I’ll likely ever know. |
<Window x:Class="SimpleAsyncCanonical.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="1*" /> <RowDefinition Height="3*" /> <RowDefinition Height="1*" /> </Grid.RowDefinitions> <TextBox Name="URI" Width="200" Height="30" Grid.Row="0" /> <TextBlock Name="WebContents" Text="Ready..." TextWrapping="Wrap" Grid.Row="1" /> <Button Grid.Row="2" Name="Go" Content="Go" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Window>
We begin by creating an HTTPWebRequest by calling WebRequest.Create and passing in the Url that the user types into our text box.
private IObservable<WebResponse> getUrl( string Url ) { var httpWR = ( HttpWebRequest ) WebRequest.Create( Url );
Without Rx we’d now call httpWR.BeginGetResponse and set in motion the two two-method asynchronous pattern. Instead, we’ll use Rx’s FromAsyncPattern<T> where T is the return type we’re looking for,
var getResponse = Observable.FromAsyncPattern<WebResponse>( httpWR.BeginGetResponse, httpWR.EndGetResponse );
This returns a function that returns an IObservable<WebResponse> which is assigned to getResponse. We then return the results of calling that function,
return getResponse( );
Calling the func actually retrieves the html as an Observable collection. We now need to translate that result into a standard string and then assign it to the Text property of the TextBlock. All of this is done in the button’s click event handler. Here is the complete source code for context,
using System; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Windows; namespace SimpleAsyncCanonical { public partial class MainWindow : Window { public MainWindow( ) { InitializeComponent( ); Go.Click += new RoutedEventHandler( Go_Click ); } private void Go_Click( object sender, RoutedEventArgs e ) { getUrl( URI.Text ).ObserveOnDispatcher( ).Subscribe( x => { var ms = new MemoryStream( ); x.GetResponseStream( ).CopyTo( ms ); string s = Encoding.UTF8.GetString( ms.GetBuffer( ) ); WebContents.Text = s; } ); } private IObservable<WebResponse> getUrl( string Url ) { var httpWR = ( HttpWebRequest ) WebRequest.Create( Url ); var getResponse = Observable.FromAsyncPattern<WebResponse>( httpWR.BeginGetResponse, httpWR.EndGetResponse ); return getResponse( ); } } }
The key is the first highlighted line, which shows the call to GetUrl passing in the Text from the text block, then ObserveOnDispatcher which marshals the results to the UI thread avoiding an exception, and then Subscribe, which subscribes to the results and modifies the results to a displayable string.
11 Responses to Asynchronous Callbacks with Rx