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

In this chapter we are going to look at using Promises to create asynchronous code that is easy to understand and hard for bugs to hide in. Specifically we are going to look at how to use the Promises that other functions return in place of callbacks.

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

Consuming Promises

Promises are a way of organizing asynchronous calls that is better than using callbacks. The callbacks are still there, but they come with a degree of organization. Promises are also the basis for the next level of making async code easier to write – async and await.

In this chapter we are going to be looking at consuming Promises.

In the next chapter we will look at how to add Promise support to your own asynchronous code i.e. how to produce Promises for others to use.

What is the problem?

As should be perfectly clear by now the problem is that JavaScript is single-threaded and this means if you were to call any functions that need to wait while some external event occurs – a file to download say – then, for the duration of the event, your app would appear to freeze. The user interface, and events in particular, are all handled by the same thread and if that thread is in a wait state then nothing else gets done.

The usual solution to this problem as explained in earlier chapters is to use a callback function. The callback is passed to the function that is going to do the long job and instead of keeping the thread waiting it simply returns immediately. This allows the thread to do other work while it get on with its task. When it has finished it calls the callback function, usually with the result of the task. The callback function then processes the results.

Callbacks are difficult because they alter the flow of control in an unnatural way and this has been explained in an earlier chapter. However, it is worth saying that the precise problem that Promises were introduced to solve is that of running asynchronous tasks one after the other.

That is, if you have three asynchronous tasks and simply call them:

TaskA();
TaskB();
TaskC();

Then they will execute in an order that depends on how long each takes. They effectively run in parallel.

If you want them to run sequentially – that is TaskB only starts after TaskA ends, and TaskC starts after TaskB ends, then you have to use some sort of mechanisms to signal the end of each task and initiate the next one in the sequence. We looked at some ways of doing this in Chapter 3, but the most basic is to use callbacks.

The callback solution is to use nested callbacks. Something like:

TaskA(callBackTaskB(callBackTaskC)));

where each task accepts a callback that is invoked when it ends. This looks simple enough in this example, but this is because it is over simplified. In real life nested callbacks quickly degenerate into "callback hell" and there is no standard way of handling errors except for having a success and a failure callback for each function.

Running asynchronous tasks sequentially is something Promises make easy.

Promises are an approach to the whole problem of working with asynchronous functions and as such they are worth knowing about.

Let's look first at the basic operation of a Promise.

The Basic Promise

An operation that takes some time will generally return a Promise object at once and then complete its task in the background:

var mypromise=slowFun(args);

Notice that even though you get a Promise object returned at once, it is unlikely that slowFun has finished whatever it is trying to do when the program moves on to the next instruction. Indeed the whole program might have come to an end before slowFun finishes.

There is also the idea that the slow function is computing a value which your program is waiting for – this isn't necessarily the case as you could be just waiting for it to complete what it is doing. However, in most cases the Promise is regarded as being a Promise to deliver a future value and this view is often helpful.

What do you do with a Promise?

The most common thing to do is to use its then method to set up what should happen when the slow function is complete:

mypromise.then(onComplete,onError);

where onComplete is a function that is called when the slow function finishes its task. It is passed the value that the slow function generates on completion. That is, when the Promise is fulfilled it supplies the value to the onComplete. The onError function is optional and is called if an error occurs while the slow function is executing. The value that the onError receives is the reason for the error.

Both the onComplete and onError functions are optional.

A Promise object is in one of three states.

  • When it is first created it is pending.

  • If the task ends correctly then it is in the resolved or fulfilled state.

  • If the task ends with an error it enters the rejected state.

A Promise is also said to be settled if it isn't pending i.e. if it is either fulfilled or rejected.

Once a Promise is settled its state and value don't change.

It is important to realize that there is no rush to use the then method, or any other method to define the functions to be called. The Promise object's state determines if and when any of the functions are called. If a Promise is already settled when you add functions as part of a then method they will still be carried out.

The key idea is that a Promise always activates the onComplete or onError after the code that is currently running has finished.

That is, a Promise always executes its functions asynchronously.

Understanding of this is essential to your understanding and use of Promises. You can be assured that nothing will happen as the result of you creating or modifying a Promise until the current thread of execution has been released. In particular, no onComplete or onError functions will be called, and as a result there is no possibility of any "race" conditions occurring. The Promise will resolve and call the functions you have specified perhaps long after your code has performed a return.

In particular, you can add as many onComplete or onError functions as you want to. For example:

mypromise.then(onComplete1,onError1); 
mypromise.then(onComplete2,onError2);

When the Promise is settled, the handlers are not necessarily called in the order that they were added.

<ASIN:1871962560>

<ASIN:1871962579>

 <ASIN:1871962528>

<ASIN:1871962501>



Last Updated ( Monday, 01 October 2018 )