Saturday, May 26, 2012

More On WinJS Promise

I did some research on WinJS Promise in my previous post. Here are some more findings:

What's the difference between Promise then and done functions?

WinJS Promise then and done both take three optional functions (onComplete, onError, onProgress) as parameters. Promise then function returns another Promise object so you can chain the Promise one by one like fnAsync().then().then()... On the other hand Promise done function doesn't return any value so it should be used at the end of the flow.

The Promise error will be thrown as an unhandled exception if there's no error handler is defined in the Promise chain even the Promise execution of that error is wrapped inside a try catch block. The difference is that the Promise then function will swallow the error and keep going forward to the next chained Promise until the error handler is found, but Promise done function would throw the error immediately. Note that the regular JavaScript exceptions (not the Promise error) remain the same behavior inside both then/done functions, and they can be handled by try catch block.

What to return from a Promise then function?

Promise then function returns a Promise. If the callback function has a return value (not a Promise), WinJS will create a Promise to return and the callback function’s return value will be passed to the next callback back function. Method addThenDouble1 and addThenDouble2 have the same result in following code snippet:
    function addAsync(number1, number2) {
        return new WinJS.Promise(function (c, m, p) {
            setTimeout(function () {
                c(number1 + number2);
            }, 1000); // Caculate after one second
        });
    }

    function addThenDouble1(number1, number2) {
        return addAsync(number1, number2).then(function (data) {
            return data * 2;
        });
    }

    function addThenDouble2(number1, number2) {
        return addAsync(number1, number2).then(function (data) {
            return new WinJS.Promise(function (c, m, p) {
                c(data * 2);
            });
        });
    }

    function test() {
        addThenDouble1(2, 3).then(function (data) {
            var value = data; // value = 10;
        });

        addThenDouble2(2, 3).then(function (data) {
            var value = data; // value = 10;
        });

        addAsync(2, 3).then(function (data) {
            return new WinJS.Promise(function (c, m, p) {
                c(data * 2);
                return 100; // this line of code has no effect
            });
        }).then(function (data) {
            var value = data; // value = 10;
        });

        addAsync(2, 3).then(function (data) { return data * 2; }).then(function (data) {
            var value = data; // value = 10;
        });
    }
We can see such logic in one of its overloading methods defined in base.js from WinJS library:
    then: function CompletePromise_then(onComplete) {
        try {
            // If the value returned from the completion handler is the same as the value
            // provided to the completion handler then there is no need for a new promise.
            var newValue = onComplete ? onComplete(this._value) : this._value;
            return newValue === this._value ? this : new CompletePromise(newValue);
        } catch (ex) {
            return new ExceptionPromise(ex);
        }
    }
The parameter sent to the next Promise.then function will be undefined if the function doesn't return anything inside the previous then function. You can create a Promise object and send as many parameters as you want as below demo:
     fnAsync().then(function (data) {
            return new WinJS.Promise(function (c, m, p) {
                // logic to get p1, p2, p3
                c(p1, p2, p3);
            });
        }).then(function (p1, p2, p3) {
            // do stuff
        });
Would it be possible to make a Promise from async to sync or block/wait a Promise to complete?

Promise function will return immediately and it relies on callback function to do the next task. Is it possible to block the Promise object until it's complete so we can get back to the classic procedural flow like following code snippet?
    function fnAsync() {
        return new WinJS.Promise(function (c, m, p) { ... });
    }
    function fnSync() {
        var promise = fnAsync();
        // Wait until promise to complete
        var syncReturn = ?promise.getReturnValueSomehow()?
        return syncReturn;
    }
The answer is no. You may think about the Promise.join function to help, but that join function is also returning a Promise. JavaScript is a single-threaded language by design, and there's no equivalent Sleep() function which can be used to check the object state periodically. So we have to use Promise's then or done function to complete the tasks flow.