JavaScript Async - Consuming Promises
Written by Ian Elliot   
Monday, 01 October 2018
Article Index
JavaScript Async - Consuming Promises
A Demo Promise
Returning A Promise
Promise Error Handling

A Demonstration Promise

One of the problems with explaining how to use Promises is that core JavaScript doesn't make any use of them. All of the standard JavaScript functions were defined well before Promises were invented and none of them return a Promise by default. Fortunately it is very easy to wrap them so that they do return a Promise, and this is part of the topic of the next chapter which demonstrates how to use Promises in your own custom functions.

Promises are used in most modern libraries such as jQuery, but to avoid having to install, use and explain a library simply to have some real function to demonstrate how Promises work, it is easier to present a small example program:

function delay(t, p) {
 var promise = new Promise( 
   function (resolve, reject) { 
     setTimeout(function () {
                    var r = Math.random();
                    if (r > p) {
                      resolve(r);
                    } else { 
                      reject(r);
                    } 
                 }, t);
  });
 return promise;
}

This function makes use of setTimeout to create a non-blocking asynchronous function that returns a Promise which resolves after t milliseconds. The second parameter determines the probability that the Promise will be rejected. You can set p equal to zero for a Promise that always succeeds, and to one for a Promise that always fails. The function returns the random number generated as its result to the Promise.

You could say that delay returns an asynchronous random number.

Don't worry how this function works at the moment, it is explained in the next chapter. For now all that matters is that you can use it to try out Promises in action.

Let’s see how it can be used in the simplest of ways:

var promise = delay(1000, 0.5);
promise.then(
             function () {
               console.log("success ");
             },
             function () { 
               console.log("fail ");
             });

The call to delay starts the timer and returns a Promise at once. Notice that even if the timer finishes nothing can happen until the program releases the UI thread to service the event queue. The Promise object has onComplete and onReject functions defined. If you run the program you will see "success" or "fail" about 50% of the time each.

Of course you can retrieve the value that the Promise returns:

var promise = delay(1000, 0.5);
promise.then(
             function (r) {
               console.log("success "+r);
             },
             function (r) { 
               console.log("fail "+r);
             });

You can only return a single value, but, of course, that value can be an object and so can return as much data as you want to pack into it.

If you don’t want to use the Promise any further there is no need to save a reference to the Promise object that is returned you could just write:

delay(1000, 0.5).then(
             function (r) {
               console.log("success "+r);
             },
             function (r) { 
               console.log("fail "+r);
             });

and this is indeed the most common Promise idiom.

At this point you might wonder what Promises have to offer over callbacks?

It is true that at this stage a Promise is very little different from a callback. The only real advantage is that it provides a standard way of setting up callbacks and the ability to set these up at any point in the program as opposed to only where the asynchronous function is called.

If this was the only advantage to using Promises we probably wouldn't bother, but a small addition to their behavior has a big effect.

Chaining Promises

At the moment it looks as if a Promise is just another way to write callbacks. After all what is the difference between writing:

slowFun(args,successCallback,errorCallback);

and:

slowFun(args).then(successCallback,errorCallback);

Looked at in this way there is no real difference and if it helps you understand the way Promises work then by all means think about them as just another way to write callbacks. However, there is a little more sophistication here than you might think and its all to do with the idiom of chaining functions.

Promises can be chained and this provides a way to run asynchronous functions sequentially.

A particular problem with asynchronous tasks is to arrange that taskB is only started when taskA has completed. In Chapter Three we looked at various ways of achieving this including the use of a queue. Promises provide yet another and arguably the best way of doing this.

If you want to perform two asynchronous tasks, one after the other, you can by simply chaining the method calls. As in all function chaining to make it work you have to return the correct sort of object from each function and in this case its another Promise.

To understand what is going on we need to master one subtle point of the way Promises work.

The then method always immediately returns a Promise object that is settled when the original Promise is.

You can think of this as a Promise linked to the onComplete or onError function specified in the then. The value of the Promise is the value that the function returns. The way that this behaves can seem complicated so we will introduce the ideas in stages – with an overview at the end.

You can use this new Promise to set up its callbacks as its onComplete and onError functions. In its turn this use of them will return a Promise that is linked to the new onComplete function and its value.

What all of this means is that by chaining Promises you can build a queue of onComplete/onError functions which will be executed sequentially in the order given by the chaining. This is very similar to the controller object introduced in Chapter Three which created a queue of functions to call. In this case it is the chain of Promises each linked to the previous Promise which forms the queue.

For example:

var myPromise1 = delay(1000, 0);
var myPromise2=myPromise1.then(
                          function (value) { 
                            console.log(value);
                            return "Hello";
                          });
myPromise2.then( 
                function (value) { 
                  console.log(value);
                });

The delay function returns a Promise object which we can use to set an onComplete using then. However, then also returns a Promise object which can have an onComplete which will be called when it is resolved.

Notice that the first function we use, i.e. delay, is slightly different from the rest in that it returns the Promise that starts the chain and it isn't executed within a then.

The value of each Promise in the chain is whatever the previous onComplete/onError returns.

If you run this you will see the random number produced by delay followed by "Hello" displayed in the console.

It is important that you really understand this example.

The key is to realize that myPromise1 and myPromise2 are returned at once by the delay and the then. This is always the case because it is what allows the code to continue executing and run to completion.

At some time in the future after the code has completed the delay finishes and myPromise1 is fulfilled and the onComplete function is fired with the Promise's value i.e. the random number. It then returns "Hello" which is the value that myPromise2 has been waiting for and it calls its onComplete function.

Of course this isn't how this code would generally be written, but it does help you to see each of the Promises in the chain.

You can write it more usually using the chaining or fluent style:

delay(1000, 0).then( 
                 function (value) {
                  console.log(value);
                  return "Hello";
                 })
              .then(
                 function (value) {
                  console.log(value);
              });

This is neater and more compact, but it is harder to see what Promise objects are in play.

<ASIN:1871962560>

<ASIN:1871962579>

 <ASIN:1871962528>

<ASIN:1871962501>



Last Updated ( Monday, 01 October 2018 )