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

Returning a Promise

So far we have been chaining together synchronous functions that return their final value quickly enough for it not to be a problem. The only asynchronous function involved in the chain has been the first one that returned the Promise that started the chain off.

If we include a non-blocking asynchronous function in the chain then it should, if it is playing fair, return a Promise so as not to hold everything up. This is where the Promise chain becomes really useful.

The rule is that if an onComplete function returns a value then this value is used as the resolved value of the Promise that the then creates. However, if an onComplete function returns a Promise then the Promise that the then creates has to wait for the first Promise to resolve.

This behavior means that the chain of function calls will execute sequentially even if it contains non-blocking asynchronous functions as long as they return a Promise.

For example, suppose one of the then functions was another call to delay:

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

What happens is almost identical to the previous example.

The first delay starts the timer and then immediately returns myPromise1. Next the then immediately returns myPromise2 and sets up the onComplete callback so that it calls delay and returns its result which is a Promise.

The code then comes to an end freeing up the UI thread. At some time later delay finishes and this resolves myPromise1 with a value equal to the random number. This causes the onComplete to start running which prints the value and starts the second delay.

The second delay immediately returns a Promise which is pending, i.e unresolved. MyPromise2 continues to wait because the Promise it has been supplied with is unresolved. MyPromise2 can only resolve if the supplied Promise is resolved, they are both waiting on the value to become available.

Eventually the second delay ends and the Promise returned is resolved and this provides a value for MyPromise2 and it too is resolved. Finally MyPromise2's onComplete is called and displays the value i.e. the second random value.

What happens when a Promise is returned to a Promise is the trickiest part of understanding Promises, but if you think of a simple Promise as waiting for a value to be available, then a Promise that is waiting for a Promise is waiting for the same final value:

promise1 -waiting> promise2 -waiting> value

when promise2 gets the value it resolves and passes the value back to promise1 which is also resolved. The principle extends to more than two Promises and you can have a chain of Promises all waiting for the final one in the chain to resolve.

Without using Promises the only way to make sure that asynchronous functions occur one after another is to nest callbacks, or you could use a function queue as described in Chapter Three.

It all depends on two properties of Promises:

  • then returns a Promise on the value returned by its onComplete or onError function

and

  • a Promise that is returned a value that is another Promise only resolves when the returned Promise does.

Chaining Promises is a fundamental way to order asynchronous tasks.

This is to be preferred to the alternative of nested Promises which is the Promise analog of nesting callbacks e.g.:

delay(1000, 0).then( 
              function (value) { 
                  console.log(value);
                  delay(500,0).then(
                    function (value) { 
                      console.log(value);
                    });
              });

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.

The important point is:

  • if you chain Promises or asynchronous functions using then they are executed sequentially.

Notice that to get things to happen in parallel you have to avoid chaining. For example to run two delays at the same time you would use:

delay(1000,0).then(
       function (value) { 
         console.log(value); 
        
       });
 delay(500,0).then(
       function (value) { 
         console.log(value); 
       })

Notice – no chaining.

Also notice that if you want to add additional handlers to the same Promise you can't use chaining.

That is:

mypromise.then(onComplete1);
mypromise.then(onComplete2);

adds onComplete1 and onComplete2 to mypromise so that they will be executed when it is fulfilled. Both onComplete functions are passed the value of mypromise i.e. the same value.

However:

mypromise.then(onComplete1).then(onComplete2);

seems to do the same thing, but onComplete1 is called by mypromise with its value and onComplete2 is called by the Promise returned by the then and its value is whatever onComplete1 returns.

Using Named Functions

Final  verion in book.

Combining Promises

Final  verion in book.

First Promise To Resolve and More!

Final  verion in book.

coverasync



Last Updated ( Monday, 01 October 2018 )