Converting between jQuery Deferred and Rx Observable

In this post, I am going to look at the similarities between jQuery Deferred and Microsoft Reactive Extensions, and supply code which will convert between the two. Although Deferred and Rx have vast differences, there are some similarities which one may wish to take advantage of. For those who have no prior knowledge of the two technologies, I’ll give a quick introduction to the basics.

jQuery Deferred

The jQuery Deferred concept is a pretty simple one. When you want to perform an asynchronous operation, such as an Ajax request or an animation, you can get a synchronous response which is a Deferred object. This object simply represents the state of that asynchronous operation.

What does one do with this Deferred object? Well, a few things:

Register for a notification when the asynchronous operation has completed (the Deferred object is “resolved”)

Combine a number of Deferred objects into a single Deferred object

Perform a specific action when the completion notification is received

Do something special when an error occurs during the asynchronous operation

A typical Deferred code snippet might look like the following, where we do something when an Ajax request is complete:

var deferred = $.ajax("hello.html");
deferred.complete(function(result){
//This function is called when the request is complete
});

Note that this only applies to jQuery 1.5 – it is the first version to include Deferred, and the Ajax function has been modified to return a Deferred object.

You can also create a custom Deferred object for something asynchronous, like for example an animation:

Microsoft Rx

Reactive Extensions is a much larger library and brings with it a whole new way of programming – “reactive” programming. This revolves around the idea of having an Observable source of items, as opposed to an Enumerable source. Therefore, we react to a new item in the collection instead of requesting one – the Observable is a ‘push’ collection as opposed to the traditional Enumerable ‘pull’ collection. I could sum up the key features as follows:

Ability to listen to a source of events (an Observable) – these could be, but aren’t restricted to, actual events

Ability to filter that Observable using a LINQ-like syntax

Ability to modify the items in the Observable, e.g., select a particular attribute of an event parameter

Combine a number of sources into a single source

Subscribe to the Observable source and react when an item is received

Handle errors as they occur during the above processing

Below is a sample piece of Rx code. This listens to three events – mouse down, mouse move and mouse up, combining them using the LINQ-like selectors to produce a “composite” stream of events – one which represents mouse moves that occur between a mouse down and mouse up. We then react to those by drawing a simple line.

Here is that powerful piece of Rx in action. Sorry Internet Explorer users – this sample uses canvas! Also, I should rightly attribute this example to Colin E who originally demonstrated it in Silverlight; this is a direct port of that Silverlight code:

Deferred vs Rx

Use a linq-like syntax to filter and transform a sequence of occurrences

Combine multiple sources to produce a single source

Handle errors in a special way

We can clearly see from this comparison one glaring difference: Rx is designed for the handling of a stream of multiple events, and jQuery Deferred handles just a single occurrence.

Given that difference, we can conclude that we can translate between Rx and Deferred in the following way:

A Deferred object can be represented as an Rx Observable with a single item

Likewise, an Rx Observable with a single item can be represented as a Deferred object

An Rx Observable with multiple items can be packaged up into a Deferred object if it is finite (i.e., we know it will complete, and on completion, we can resolve the Deferred)

A continuous Observable that does not complete, e.g., mouse move events, cannot be represented as a Deferred because we would not know when to resolve the Deferred.

Converting Deferred to Rx

As pointed out above, this conversion is pretty simple: a Deferred object can be represented as an Rx Observable that spits out a single item and then is completed.

function DeferredAsObservable(deferred) {
//Create a new observable. The AsyncSubject class is a type of Cold observable,
//which means that when a user subscribes to the observable, they get items that
//occurred before the subscription.
var observable = new Rx.AsyncSubject();
//When the Deferred is complete, push an item through the Observable
deferred.done(function(){
//Get the arguments as an array
var args = Array.prototype.slice.call(arguments);
//Call the observable OnNext with the same parameters
observable.OnNext.apply(observable, args);
//Complete the Observable to indicate that there are no more items.
observable.OnCompleted();
});
//If the Deferred errors, push an error through the Observable
deferred.fail(function(){
//Get the arguments as an array
var args = Array.prototype.slice.call(arguments);
//Call the observable OnError with the args array
observable.OnError.apply(observable, args);
observable.OnCompleted();
});
return observable;
}

Converting Rx to Deferred

The conversion from Obervable to Deferred is a little more complicated since there could be multiple items in the Observable. To get around this, we collect all of the items that the Observable ‘pushes’ out until we get a Complete notification. Then we resolve the Deferred with all of those items.

We can use the earlier example of the drawing applcation to show this conversion. Notice the call to ‘Repeat()‘ at the end of the draggingEventsObservable – this caused us to repeatedly listen for mouse moves that occurred between a mousedown and a mouseup. If we omit that, then we can just listen to a single line draw (a finite sequence) and convert that to a Deferred object:

But what if we left in the call to Repeat(), and the Observable doesn’t complete? That type of Observable doesn’t translate to the Deferred concept – what use is a Deferred object that is never resolved? In this case, we can only assume that the programmer has made a mistake. Unfortunately, there is no way to detect this scenario so we’ll just let the programmer work it out the hard way!

Rx for jQuery

While we’re on the topic of combining Rx and jQuery, I should point out that Microsoft has already provided us with a library of helper functions to perform the most common tasks – rx.jQuery.js. It extends jQuery with the following functions:

In the above scenarios, rx.jQuery.js provides us with Observable versions of a number of places where you may otherwise have the implement the Deferred to Rx code I have given above. Unfortunately, they aren’t brilliantly documented but it is easy enough to inspect the functions in a debugger to deduce their parameters.

The End

I hope you’ve enjoyed this largely academic guide to conversion between Rx and Deferred. Have fun!

Share

About the Author

Repstor’s products enable the successful adoption of content management systems within organizations. Repstor affinity provides uninterrupted access to content systems, like SharePoint through the familiar interface of Microsoft Outlook. Repstor assist provides facilitated filing for all users by suggesting the most suitable location for new content based on best practice filing.

Comments and Discussions

This is a nice article comparing and contrasting Deferred and RX. One thing to mention in your discussion of converting Observables into Deferred is that it is not uncommon for an Observable to never complete. In that case, your implementation will never get called. You would need to fire and reregister multiple deferreds in the subscription in order to overcome that.