Introduction to the Reactive Extensions for JavaScript – Composing deeper

We’ve covered a bit of ground already in this series on the Reactive Extensions for JavaScript (RxJS) from the basic information, creating observers and observables, jQuery integration, composing asynchronous methods with callbacks and in the last post, turned blocking calls into asynchronous calls.  Now that we have some more fundamental building blocks, let’s see what else we can do with it.  Before moving to FlapJax examples, I want to revisit the Microsoft Translator to take a piece of text and translate into all languages except the currently detected.

Also, if you’re at Mix this year, you’ll notice that Erik Meijer is giving a talk on the Reactive Extensions for JavaScript. I highly recommend that you go to this session, especially if you’ve been interested in this series.  Prepare for some pretty slick demos to show off the functionality of the library.

Before we get started, let’s get caught up to where we are today:

Composing and Filtering

As I stated earlier, the goal of this post was to take our piece of text, detect the language and then translate it to all other languages other than the detected.  We’ll make use of the detect, getLanguages and translate methods as we’ve talked about earlier, but with some slight changes.  Since the time I wrote these posts, the Microsoft Translator has released a new API for us to call which gets away from the JavaScript library approach and to the URL based approach instead.  You can read more about the APIs in their documentation.  Let’s get started taking our old code and porting it to the new APIs.  First, I’ll need the base URL and an AppID that you can apply for.  And next, I’ll need to handle making an XmlHttpRequest, getting the value and then evaluating the response.  Yes, I realize that eval might be used for malicious purposes, but in this case, this is simple text being returned.

var url = "http://api.microsofttranslator.com/V2/Ajax.svc/";
var appId = "someappid";

function getResponse(url) {
    return Rx.Observable.XmlHttpRequest(url)
        .Select(function(result) {
            return eval(result.responseText);
        });
}

Now we can turn our attention once again to the getLanguages function which gives us all of the language codes that are currently supported by the Translator.  To do this, we build up a URL for our request, send it to the getResponse function, and then project each language array of our observable sequence to an observable sequence, in this case our resulting array turned into an observable, and flattens the resulting observable sequences into one observable sequence.

function getLanguages() {
    var serviceUrl = url +
        "GetLanguagesForTranslate?appId=" +  appId;

    return getResponse(serviceUrl)
        .SelectMany(function(result) {
            return result.toObservable(); });
}

Our detect follows the same pattern of building up a URL and gets the response from the XmlHttpRequest as an observable sequence.

function detect(text) {

    var serviceUrl = url + 
        "Detect?AppId=" + appId + 
        "&text=" + encodeURIComponent(text);
        
    return getResponse(serviceUrl);
}

And translate follows the very same pattern as well.

function translate(text, from, to) {

    var serviceUrl = url +
        "Translate?appId=" +  appId +
        "&from=" + from +
        "&to=" + to +
        "&text=" + encodeURIComponent(text);

    return getResponse(serviceUrl);
}

We’ll start first at creating a subscription for the translation button to listen for clicks:

$("#translateCommand")
    .ToObservable("click")
    .Subscribe(function(event) {
    
    // More stuff here
    
});

Inside of the Subscribe method, we need to clear out the last results as well as get the value of the text to be translated.

$("#translatedText").empty();
var textTotranslate = $("#translateText").val();

Next, we need to add more code to do the actual translation.  What we’re going to do is first detect the text and before we get to combining this with another function. 

var translator = detect(textTotranslate)

I’m going to need to take the detected language and then translate it into all the other languages.  In order to do that, I need to use the SelectMany method which lets you project our detected text to another observable sequence, which in this case will be the languages. 

.SelectMany(function(detectedLanguage) {
    // Code goes here
});

Once we get to the languages, we want to filter out the language that equals our detected language, and then once again, project our language to the translate method observable sequence. 

return getLanguages()
    .Where(function(language) { return language != detectedLanguage; })

I’ll take the result of the translated text and then project to return an object with a translated property with our translated text, and the associated language property. 

.SelectMany(function(language) {
     return translate(translatedText, detectedLanguage, language)
        .Select(function(translatedText) {
            return { translated : translatedText, language : language };
 });

All together, the code to take the piece of text, detect its language, and translate it into all other supported languages looks like the following.

var translator = detect(textTotranslate)
    .SelectMany(function(detectedLanguage) {
        return getLanguages()
            .Where(function(language) { return language != detectedLanguage; })
            .SelectMany(function(language) {
                return translate(translatedText, detectedLanguage, language)
                    .Select(function(translatedText) {
                        return { translated : translatedText, language : language };
                });
        });
});

Then we could subscribe to this observable sequence and for example, take a sentence “Do you know what time it is?” and translate it into 29 other languages.

image

What’s really interesting about this example is the true asynchronous behavior.  If I press the button again with the same sentence, the order of their return is not always the same each and every time.  

Conclusion

Through the use of the Reactive Extensions for JavaScript, we’ve been able to compose together asynchronous behavior of detecting a piece of text’s language, and translating it into 29 different languages all at once.  This could apply to any number of scenarios where we need to compose together one asynchronous call after another, but only when certain things happen.  And better yet, to use code in a fluent API style, you don’t have this logic scattered throughout all of your functions.

This of course is only scratching the surface of what capabilities this library has and there is much more yet left to cover.  The question you’re probably asking now is where can I get it?  Well, for that you’ll have to stay tuned to Erik Meijer and his Reactive Extensions for JavaScript session at Mix on Wednesday.

What can I say?  I love JavaScript and very much looking forward to the upcoming JSConf 2010 here in Washington, DC where the Reactive Extensions for JavaScript will be shown in its full glory with Jeffrey Van Gogh (who you can now follow on Twitter).  For too many times, we’ve looked for the abstractions over the natural language of the web (HTML, CSS and JavaScript) and created monstrosities instead of embracing the web for what it is.  With libraries such as jQuery and indeed the Reactive Extensions for JavaScript gives us better tools for dealing with the troubled child that is DOM manipulation and especially events.

1 Comment

Comments have been disabled for this content.