JavaScript Async - Returning Promises
Written by Ian Elliot   
Monday, 15 November 2021
Article Index
JavaScript Async - Returning Promises
Currying

Promises are still relatively new and this means that there are asynchronous functions that don't make use of them. This leads on to the need to promisify existing and future code. In this extract from my book on JavaScript Async we look at some of the inner workings of the promise.

This is an extract from the recently published JavaScript Async: Events Callbacks, Promises & Async/Await.

Now Available as a Book:

 JavaScript Async

cover

You can buy it from: Amazon

Contents

  1. Modern JavaScript (Book Only)
  2. Events,Standard & Custom
  3. The Callback
      extract - The Callback & The Controller
  4. Custom Async - setTimeout, sendMessage & yield
      extract - Custom Async
      extract - Avoiding State With Yield 
  5. Worker Threads
      extract - Basic Worker ***NEW
      extract - Advanced Worker Threads 
  6. Consuming Promises 
  7. Producing Promises
      extract - The Revealing Constructor Pattern
     
    extract - Returning Promises
     
    extract - Composing Promises
  8. The Dispatch Queue
      extract - Microtasks
  9. Async & Await
      extract -  Basic Async & Await
      extract -  DoEvents & Microtasks
  10. Fetch, Cache & Service Worker
      extract - Fetch  
      extract - Cache
     
    extract -  Service Workers

After learning how to use or consume Promises in the previous chapter the next step is to add support for Promises to asynchronous functions and to work with them to create new Promise features.

Included in chapter but not in this extract

  • The Problem With Promises
  • The Revealing Constructor Pattern

The Promise Mechanism

When you create a standard Promise you use its constructor and you pass it a function, the executor, that is immediately executed by the constructor. This is the function where you create the asynchronous task and then call resolve or reject accordingly – usually when the asynchronous task has completed.

In other words this is the code that does the work.

For example, the delay function example introduced in the previous chapter can be written using JavaScript Promises as:

function delay(t) {
 var p = new Promise(
               function (resolve, reject) {
                 setTimeout( 
                    function () { 
                      resolve();
                    }, t); 
               });
 return p;
}

You can see that it has the same basic structure, the only difference is that now the code calls the private resolve and reject functions that are defined within the constructor. The constructor executes this immediately, passing it the private resolve and reject functions and returns the Promise.

Notice that within the function that you pass to the constructor, the calls to resolve and reject result in calling all of the onComplete and onError functions that the consumer of the Promise has set up using the then method of the returned Promise object. Only call resolve or reject when the asynchronous task has completed, and return its value or error code in resolve and reject.

Now we are in a position to understand the demonstration Promise introduced in the previous chapter:

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;
}

You can see that what happens is we create a new Promise object which is returned to the caller almost at once. We also use setTimeout to place a function on the event queue which when its time is up calls either the resolve or the reject function with the specified probability passing the random number back as the resolved value.

coverasync

The Then Parameter Problem

As delay returns a Promise, it seems obvious that it could be used in a chain (see the previous chapter). However, there is a problem.

If you try:

getTime();
delay(1000,0)
  .then(getTime)
   .then(delay(1000,0)) 
    .then(getTime);

where getTime is something like:

function getTime() {
  var time = new Date().getTime();
  console.log(time);
}

which shows a timer count in milliseconds, what you discover is that it appears to work, but if you look carefully the getTime functions report times that are only a few milliseconds apart, rather than 1000ms apart.

The reason should be easy to spot. The function being passed to the then function has parameters:

.then(delay(1000,0)) 

and this means the function is evaluated at once and not passed to the then to be activated at a later time.

The problem is that you cannot pass a parameter to a function that you use in a then.

Notice that when it is called by the Promise, the function may be passed any number of parameters depending on the way the Promise is settled.

There are a number of solutions to the problem, but none are 100% satisfactory.

The first, and most obvious, is not to use a parameter at all, but this would result in a delay function that gave a fixed time delay and this generally isn't what you want.



Last Updated ( Monday, 15 November 2021 )