Friday, May 04, 2012

A Study of WinJS Promise

.NET 4.5 introduces a new async/await asynchronous programming pattern. Metro Javascript library WinJS also has a similar asynchronous programming paradigm called Promise. Promise is an Javascript object with a "then" function that takes three parameters: a complete callback function, an optional failure callback function, and an optional on progress callback function. We can do something like:
    function getWebContentPromise(url) {
        WinJS.xhr({ url: url }).then(
            function (result) { // Successfully
                console.log("Get web content successfully. Data length:" + result.responseText.length);
            },
            function (err) { // Failed
                console.log("Get web content failed. Error:" + err);
            },
            function (prog) { // On progress
                console.log("Get web content on progress...");
            });
    }
I tested above script in Visual Studio 11 Beta. WinJS.xhr({ url: url }) returns immediately, and those "then" callback functions are hit later when the response content is ready or error occurs due to network issue. Looks good so far. How can I create my own Promise and asynchronous function? Say I have a simple script as follow:
    function complicatedCalculation(x, y) {
        var result = x + y;
        // fake busy work...
        for (var i = 1; i < 10000000; i++) {
            var dummy = (i * i) / i;
        }
        return result;
    }

    function getResult(x, y) {
        var result = complicatedCalculation(x, y);
        return result;
    }
It's just a heavy function that would block the UI for a while. So how can we convert it to be Promise-able? From MSDN documentation, I found Promise.as and Promise.wrap functions, and both could wrap an object in a Promise object. Unfortunately the documentation is super simple without any sample code or detail explanation, basically just useless. Forgive that since WinJS is still in Beta. Anyway I tried:
    WinJS.Promise.wrap(complicatedCalculation(x, y)).then(...
It works but it's not asynchronous, i.e. WinJS.Promise.wrap(complicatedCalculation(x, y)) doesn't return immediately, instead "then" callback functions are invoked only after the complicatedCalculation function is completed. That's not what we want. So I manually convert above method to the Promise object, whose constructor accepts three parameter, a complete callback function, an optional error callback function, and an optional on-progress callback function:
    function complicatedCalculationPromise(x, y) {
        return new WinJS.Promise(function (completeCallback, errorCallback, progressCallback) {
            try {
                var result = x + y;
                // fake some busy work here...
                for (var i = 1; i < 10000000; i++) {
                    var dummy = parseInt((i * i) / i);
                    if (progressCallback && dummy % 1000000 == 0)
                        progressCallback(dummy);
                }
                completeCallback(result);
            } catch (e) {
                errorCallback(e);
            }
        });
    }

    function getResult(x, y) {
        complicatedCalculationPromise(x, y).then(function (result) {
            return result;
        });
    }
Above code compiles okay and works without any exception. But it's still a synchronous function. The fuction complicatedCalculationPromise blocks the operation until the calculation is completed. I guess it's due to the fact that Javascript is single-threaded. Why WinJS.xhr could be asynchonous? The reason is that inside the xhr function, an XMLHttpRequest is created, a callback for response change event is registered, the request is sent out and then xhr method will return immediately without waiting the response from network. XMLHttpRequest supports asynchronous callback natively. WinJS.xhr is simply a wrapper of XMLHttpRequest and exposes it as a Promise object.

To simulate an asynchronous processing in Javascript, we could modify the script as below:
    function complicatedCalculationAsync(x, y) {
        return new WinJS.Promise(function (completeCallback, errorCallback, progressCallback) {
            var count = 0;
            var result = x + y;
            try {
                setTimeout(function () {
                    count++;
                    if (count == 10000)
                        completeCallback(result);

                    var dummy = parseInt((count * count) / count);
                    if (progressCallback && dummy % 1000 == 0)
                        progressCallback(dummy);

                    setTimeout(arguments.callee, 0);
                }, 0);
            } catch (e) {
                errorCallback(e);
            }
        });
    }
Now the function becomes real asynchronous. But we have to partition the work manually and use setTimeout timer function to hand back control to Javascript execution thread periodically. All those hustles are due to the limitation of single-threaded Javascript model for UI processing. In HTML5 Web Workers allows you to load the Javascript dynamically and run it in background thread, and you can pass result back to the UI thread. I hope that when WinJS.Promise is final released, it could have an option to integrate with Web Workers so asynchronous execution can be run in a separate background thread. That would be a big lift in my opinion.