WinRT JavaScript - Promises
Written by Ian Elliot   
Article Index
WinRT JavaScript - Promises
An Example Promise
Custom Promises

An Example Promise - timeout

One of the difficulties of demonstrating how promises work is that it is too easy to get confused by what the functions are doing that are returning promises.

One very easy-to-understand promise is generated by the static promise.timeout method. This comes in two forms:

Promise.timeout(timeout);

returns a promise that will complete after the number of milliseconds specified in timeout. The second isl

Promise.timeout(timeout,promise2);

This forces promise2 to complete after the specified time. It can be used to set a timeout for a promise.

Of course a timeout promise can be used to do something after an asynchronous wait - it is promise wrapper for setTimeout.

For example:

var mypromise = WinJS.Promise.timeout(1000);
mypromise.then(function (value) {
     console.log("promise1 complete); });

If you run this you will see the message appear on the console after 1 second. 

You can also use the timeout promise to demonstrate one of the truths of asynchronous programming - things don't happen in the order they appear to. For example

var promise1 = WinJS.Promise.timeout(4000);
promise1.then(function (value) {
     console.log("promise1 complete"); });
var promise2 = WinJS.Promise.timeout(2000);
promise2.then(function (value) {   
     console.log("promise2 complete"); });

A simple reading of the program might suggest that the promise1 message appears first but if you know your promises it is obvious that "promise2"  message appears after 2 seconds and the "promise1" message appears after 4 seconds. In general when you set asynchronous operations off you can't be sure in what order they will complete.

Chaining Promises

If you want to perform two asynchronous tasks one after the other you can by simply chaining then method calls. As in all function chaining to make it work you have to remember to return the correct object sort of object from each function.

Now to understand what is going on we need to master one subtle point of the way promises work. The then method also returns a promise which has the value returned by the onComplete function. Given it returns a promise object we can use then a second time and so on. For example:

WinJS.Binding.processAll().then(function (value) {
                return "hello";
            }).then(function (value) {
                console.log(value);
            });
          

Now if you run this you will see "hello" displayed in the console.

However if the value you pass back is a promise then this is returned by the then method.

Using our example timeout promise you can see this in action

WinJS.Promise.timeout(4000).then(function (value) {
   console.log("promise1 complete");
   return WinJS.Promise.timeout(2000);
}).then(function (value) {
        console.log("promise2 complete");
        });

In this case the first timeout promise takes 4 seconds to complete. It then returns a timeout promise that takes 2 seconds. What you see is the promise1 message appear after 4 seconds and the promise2 message appearing 2 seconds after that. Compare this with the earlier example where the two promises weren't chained.

Finding a simple but more realistic example of this is difficult but as well as the WinJS.UI.processAll function there is a WinJS.Binding.processAll function which scans the DOM for data bound elements. This too returns a promise object so to ensure that they run one after another you would use:

WinJS.UI.processAll().then(function (value) {
           console.log("controls set up");
           return WinJS.Binding.processAll();
           }).then(function (value) {
                console.log("data binding done");
              });

Notice that is is to be preferred to dealing with the promise object within the first onComplete functions. That is don't write things like:

WinJS.UI.processAll().then(
  function (value) {
   console.log("controls set up");
   WinJS.Binding.processAll().then(
    function (value) {
      console.log("data binding done");
    });

  });

 

This nests a callback within a callback. It is better to return the promise object and write things like:

 object.then(oncomplete1).then(oncomplete2)

and so on.

There is also a done method which is to be used as the last operation in a chain of then calls. It doesn't return a promise object and throws an exception if an error occurs and there is no onError method.

Joining Promises

Suppose you want to start two slow functions doing their job and want to do something when they are both complete - how can you do this?

The answer is that you can save the promise object returned by each function and the static join method to create a single promise object that is only complete when both promises are complete. For example, using our timeout example again:

var promise1 = WinJS.Promise.timeout(4000);
var promise2 = WinJS.Promise.timeout(2000);
var totalPromise = WinJS.Promise.join(
                       [promise1, promise2]);
totalPromise.then(function(value){
         console.log("both promises finished");
 });

Notice that the two promises are formed into a a singe promise object that prints the message after 4 seconds is up. The promise objects are packaged as an array and they don't wait for each other as in the chaining example. 

If you want the results from each of the promises then package the promises not as an array but as an object with properties and values - the values are the promises. For example:

var totalPromise = WinJS.Promise.join(
      { task1: promise1, task2: promise2 });

In this case the then method returns a promise with a value that is an object with the same properties but with values that are the completion results of each of the tasks. That is the promise's value has properties value.task1 and value.task2 containging the result value of the corresponding promises.

Promises In Events

One small complication is what happens if you use a promise within an event handler. In this case the system will think that the event is complete but in fact your code is waiting on the completion of the promise. To tell the system that the event isn't complete until the promise is complete you can use the setPromise method of the event parameter. For example in the latest template you will find the line:

args.setPromise(WinJS.UI.processAll());

where it used to say just processAll. This causes the system to not remove the splash screen that is displayed until your UI is ready to display until the promise is complete.

You will find that using setPromise is a good idea in most event handlers. Also notice that if you want to use the then method of the promise object you can:

args.setPromise(WinJS.UI.processAll().next(...));