Introduction to the Reactive Extensions for JavaScript – New Release and Joins
This past week there was a new release of the Reactive Extensions for JavaScript which includes many of the changes I’ve been talking about lately including the third-party library integration, aggregates which I covered in the previous posts, and joins which is the subject of today’s post.
Before we get started, let’s get caught up to where we are today:
- Introduction
- Creating Observables
- Creating Observers
- jQuery Integration
- Composing Callbacks
- From Blocking to Async
- Wikipedia Lookup
- Composing Deeper
- Bing Maps and Twitter Mashup
- Drag and Drop
- jQuery Live Event integration
- jQuery AJAX integration
- A Separation of Concerns
- Aggregates – Part 1 and Part 2
Joins
When we talk about joins in the Reactive Extensions for JavaScript, and for that matter in the .NET version as well, we’re not talking about the kind that we find in LINQ to Objects which matches two sequences on a given key. Instead, what we’re talking about is to define some complex synchronization patterns between multiple event sources where we create composite events based upon combinations of other events. In the Reactive Extensions for JavaScript, there is an implementation of Join calculus over observable sequences which allows for us to create a set of patterns that match based upon which notifications come in.
Rx.Observable.Join = function( one to many plans);
So, that brings us to the point, what is an execution plan exactly and what does it look like? Well, an execution plan consists of a pattern of two functions, an And function and Then function. Let’s first take a look at the And function, which matches when all observable sequences have a value.
Rx.Observable.And = function( other); // The other observable to match
We can then follow this And with the Then function which matches when all observable sequences have their value available and then projects its value to another structure.
Rx.Observable.Then = function( selector); // Projects the match value
Let’s create a simple example to show how this works. In this example, we’ll listen for certain keys and when both of them are pressed in any given order, then we’ll bubble up and event. We’ll listen to the keyup event and check for the C and O characters in the respective observable collections.
var cKey = $("#textBox") .toObservable("keyup") .Where(function(key) { return String.fromCharCode(key.keyCode) === "C"; }); var oKey = $("#textBox") .toObservable("keyup") .Where(function(key) { return String.fromCharCode(key.keyCode) === "O"; });
Next, we’ll create a pattern which will fire when the combination of C and O has been fired by using the Rx.Observable.Join function. Inside the Join function, we’ll create a pattern of the cKey and the oKey together, and then project those two events together to return a string indicating it fired.
var keyJoin = Rx.Observable.Join( cKey.And(oKey).Then(function(c, o) { return "CO pressed!"; }));
Then we can subscribe to the results of this join to alert when I’ve typed the letters C and O in the textbox. Note that the two keys don’t have to be in a certain order and there can be keys in between.
keyJoin.Subscribe(function(event) { alert(event); });
We can extend this example to add another pattern in which we listen for the letters D and E in our textbox and fire a different notification. To make that happen, we simply add another pattern to the Join method. We can add as many Join patterns as we wish to get the right behavior our application needs.
var dKey = $("#textBox") .toObservable("keyup") .Where(function(key) { return String.fromCharCode(key.keyCode) === "D"; }); var eKey = $("#textBox") .toObservable("keyup") .Where(function(key) { return String.fromCharCode(key.keyCode) === "E"; }); var keyJoin = Rx.Observable.Join( cKey.And(oKey).Then(function(c, o) { return "CO pressed!"; }), dKey.And(eKey).Then(function(d, e) { return "DE pressed!"; })); keyJoin.Subscribe(function(event) { alert(event); });
Let’s look at another example, this time let’s take an example where we have three pending transactions, but only really care if two of them succeed. In this case, we’ll port one of Erik’s Microsoft Translator examples where those transactions will be going to the Microsoft Translator and then translating a given sentence into three languages, but we only care if two returns and we don’t care which. Let’s first start off by defining our translate function using the integration with jQuery and the Reactive Extensions for JavaScript to translate some text from one language to another.
function translate(from, to, text) { var appId = "someappid"; var serviceUrl = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate"; return $.getJSONAsObservable( serviceUrl, { appId : appId, from : from, to : to, text : text }); }
Now we’re going to kick off the action when our translate button has been clicked.
$("#translateButton") .toObservable("click") .Subscribe(function() {
Then we’re going to get the text and call the translate in the three different languages, Dutch, Spanish and French.
var text = $("#textToTranslate").val(); // Let's do some translating! var from = "en"; var dutch = translate(from, "nl", text); var spanish = translate(from, "es", text); var french = translate(from, "fr", text);
Now comes the interesting part of the post. In order for us to check for any two of our language translations to complete, we need three different execution plans, one for Dutch and Spanish, another for Dutch and French and finally one for Spanish and French. To create those execution plans, we’ll take our first translation observable and call And, passing in the other translation observable, and then finally call the Then function which projects the first and second language. In the Then function, we’ll project a structure which contains properties for all three languages but two get the translation and the other gets a failure message.
// Joins var fail = "FAIL!"; var results = Rx.Observable.Join( dutch.And(spanish).Then(function(d, s) { return { dutch : d.data, spanish : s.data, french : fail };}), dutch.And(french).Then(function(d, f) { return { dutch : d.data, spanish : fail, french : f.data};}), french.And(spanish).Then(function(f, s) { return { dutch : fail, spanish : s.data, french : f.data};}));
Now that we created this set of patterns, we can then subscribe via the aptly named Subscribe method which passes our structure with the properties for all three languages. We’ll take that structure and print out the value of each translation, whether it succeeded or not.
results.Subscribe(function(result) { $("#spanish").html("Spanish: " + result.spanish); $("#french").html("French: " + result.french); $("#dutch").html("Dutch: " + result.dutch); });
Now, let’s take our code and check it out in the browser. Once I enter a phrase I wish to translate and click the Translate! button, you can see that both Spanish and Dutch return their value, but French does not.
And then I could press the button again, and sure enough, this time Spanish fails while both Dutch and French translations return a proper value.
So, as you can see, this gives us some pretty interesting possibilities to create execution plans
Conclusion
As you can see, with the Aggregates library addition of the Reactive Extensions for JavaScript, we have yet another tool in our belt for handling our data. That’s just one of the many things we can do with it that I’ll hopefully cover more in the near future. So, download it, and give the team feedback!