Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

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.

This entry was posted in Event-based Porgramming, JavaScript, JSConf, Reactive Framework. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.servicestack.net/mythz_blog/ Demis Bellot

    Hey awesome response thanks!
    Exactly what I was looking for!

    As for the nested callbacks comment, I only meant that I am looking into RxJs to try get out of ‘nested callback hell’ with my API that is normal with Async JavaScript API’s. So on the face of it, RxJs didn’t look like much of an improvement in that area.

    And to answer your last Q, yes you should turn this into a post 😉

    – Demis

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Demis,

    Yes, I should move getLanguages() out and have it cached at this point, and that’s a good point because it’s an operation that should only happen once.

    I’m not sure I get the complaint about the nested callbacks. After all, I need to chain together the transformations of taking one computation and creating another based upon it and to do that, you are providing projections which then get invoked on the OnNext call.

    Now to answer your question in regards to your services to compose getValue1 and getValue2, you could use the following:

    Now your ultimate goal for your webservices should be something like the following using RxJS:

    asyncService.getValue1AsObservable(“1″)
    .SelectMany(
    function(result) {
    return asyncService.getValue2AsObservable(result); })
    .Subscribe(function(result) { alert(result); });

    In the parlance of using LINQ expressions would be:

    from result1 in asyncService.getValue1AsObservable(“1)
    from result2 in asyncService.getValue2AsObservable(result1)
    select result2;

    Alternatively, if you know each one is going to project one and only one value, you could use a Select/Switch, but SelectMany in this instance makes more sense. But that would look like the following:

    asyncService.getValue1AsObservable(“1″)
    .Select(
    function(result) {
    return asyncService.getValue2AsObservable(result); })
    .Switch()
    .Subscribe(function(result) { alert(result); });

    Now the question of how to wrap the services, you should use the AsyncSubject to return the value and handle the associated success and error cases:

    asyncService.prototype.getValue1AsObservable = function(data) {
    var subject = new Rx.AsyncSubject();

    var onNext = function(next) {
    subject.OnNext(next);
    subject.OnCompleted();
    }

    var onError = function(err) {
    subject.OnError(err);
    }

    asyncService.getValue1(data, next, err);

    return subject;
    }

    Hope that helps! I should probably turn this into a post as well.

    Matt

  • http://www.servicestack.net/mythz_blog/ Demis Bellot

    Does this code make an unnecessary network ajax call to ‘getLanguages()’ everytime? If it does, it’s not a great idea to promote bad practices for the sake of a more elegant example as sooner or later we’re going to have to use this stuff in the real world.

    Another problem I have is the number of nested callbacks that still needs to be done with an RxJs solution as I count 3 in the last example.

    RxJs definitely has superior composability and can be more elegant at times but the concept and write-ability is still harder to grasp then with straight-forward async callbacks.

    Anyway it’s still early days so I hope you guys have a lot more examples how we can Rx-ify our async web service calls. Specifically I have an async service that look something like:

    function AsyncService(url)
    {
    this.url = url;
    }
    AsyncService.prototype = {
    getValue1 = function(input1, onSuccessFn, onErrorFn) {
    },
    getValue2 = function(input2, onSuccessFn, onErrorFn) {
    }
    };

    //So at the moment composing the results of 2 calls would look like:
    var asyncService = new AsyncService(“http://location/”);
    asyncService.getValue1(“1″, function(result1) {
    asyncService.getValue2(result1, function(result2) {
    alert(result2);
    };
    });

    It would be nice to know how I could Rx-ify my async service, i.e. what the boiler plate I would require look like. Also how the above example would look like in Rx?