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

 

Promise Error Handling

One of the big advantages of using Promises is that they make error handling easier. Not easy, but easier. Asynchronous error handling is never easy because errors can occur in a function while other functions are still executing. There is generally no way of canceling asynchronous functions, and this can make it difficult to work out what to do. However, the main problem Promises solve is detecting when something has gone wrong.

We already know that you can specify an error handler as part of the then function.

promise.then(processFirstFile(value), handleError1(error));

The handleError function will be called if the get fails for any reason i.e. if it returns an error status.

This is simple, but things get a little more complicated when we have a chain of thens. If you are running one asynchronous task after another then we have already discovered that you can do this by chaining thens.

For example:

var promise2=promise1.then(onSuccess1,onFailure1);
var promise3=promise2.then(onSuccess2,onFailure2);

where the fluent style has been avoided to show the explicit Promises returned. The first then returns promise2 that is settled when onSuccess1 returns a value or a settled Promise.

It is also helpful to keep in mind that in say:

var promise2=promise1.then(onSuccess1,onFailure1);

it is the settlement state of promise1 that determines which of onSuccess1 or onFailure1 are executed and what onSuccess1 or onFailure1 return that determines the settlement state of promise2.

However, there is an additional rule governing chained Promises. If there is no onSuccess or onFailure to handle the settlement of the Promise, then that state is passed to the next Promise and so on until there is a handler for the state.

That is, an unhandled state is passed to the next Promise in the chain.

This intentionally copies the way that exceptions work in synchronous code.

So for example if we have, writing the chain out in full for clarity:

var promise2=promise1.then(null,onFailure1);
var promise3=promise2.then(onSuccess2,onFailure2);

and promise1 is fulfilled there is no onSuccess handler defined in its then. What happens is that this state is passed to promise2 which is fulfilled and onSuccess2 is executed. Notice that the final state of promise2 would have been determined by the missing onSuccess1 handler so passing the state on is reasonable as a default.

The same rule applies to the rejection handlers. If there is no rejection handler and the Promise is rejected then that state is passed to the next handler in the chain.

Once the state has found a handler then processing resumes its normal course.

But to make sense of "normal course" we need one final rule.

Any handler that returns a value and does not throw an error passes on a success to the next Promise and this includes onFailure handlers. Any handler that throws an exception passes a reject on to the next Promise.

This all seems complicated, but the rule is that states are passed on if there is no handler for the state, and any handler that returns a value and doesn't throw an exception passes on a success to the next Promise.

For example:

var promise2=promise1.then(onSuccess1);
var promise3=promise2.then(onSuccess2,onFailure2);
var promise4=promise3.then(onSuccess3);

which would normally be written:

promise1.then(onSuccess1)
        .then(onSuccess2,onFailure2)
        .then(onSuccess3);

Suppose promise1 is rejected. As it has no onFailure handler, the rejection is passed on to promise2 which causes onFailure2 to run. Assuming onFailure2 returns a value and doesn't throw an exception, promise3 is fulfilled and onSuccess3 runs. You can think of this as a successful run of onFailure2 keeps the sequence of operations going. If this isn't what you want then throw an exception in onFailure1.

In most cases it is reasonable to leave any rejection of any Promise in a chain of Promises till the very end so aborting the chain.

For example:

var promise2=promise1.then(onSuccess1);
var promise3=promise2.then(onSuccess2);
var promise4=promise3.then(onSuccess3);
var promise5=promise4.then(null,onFailure4);

If any of the Promises are rejected then the subsequent tasks are not started and the next function to be executed is onFailure4 which is a catch all error routine.

This is such a common idiom that there is a special catch function which just sets a reject handler. So you could write the above as:

var promise2=promise1.then(onSuccess1);
var promise3=promise2.then(onSuccess2);
var promise4=promise3.then(onSuccess3);
var promise5=promise4.catch(onFailure4);

Of course even this isn't the usual way to write this because we use chaining in the fluent style:

promise1.then(onSuccess1)
        .then(onSuccess2)
        .then(onSuccess3)
        .catch(onFailure4);

which now looks a lot more like the synchronous try-catch block that it models.

To be clear, if there is a problem in any of the tasks say in onSuccess2 then effectively control jumps straight to onFailure4.

For example:

delay(1000, .5) 
      .then(
          function (value) {
            console.log("success1");
            console.log(value);
            return delay(1000, .5);
          })
      .then(
          function (value) {
            console.log("success2");
            console.log(value);
          })
      .catch(
          function (value) {
            console.log("fail");
            console.log(value);
          });

In this case if either of the delay functions fail we see fail printed. So the possibilities are success1, success2 or success1,fail or just fail.

The Then Promise Chain

We are now in a position to characterize everything that there is to know about then and the Promise it returns.

Final  verion in book.

Summary

  • Instead of accepting callbacks, asynchronous functions can and do return Promises.

  • You can add the equivalent of onComplete and onError callbacks to the Promise using the then function.

  • 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. When a Promise is settled it cannot thereafter change its state.

  • Handlers are called asynchronously when the Promise is settled. Any handlers that are added after the Promise is settled are also called asynchronously.

  • The then function returns a new Promise which is fulfilled if its onComplete handler returns a value. If its onComplete handler returns a Promise, this Promise determines the state of the Promise returned by the then.

  • Notice that in a chain of Promises the fulfillment state of a Promise determines which of the handlers it then executes and the result of the handler determines the state of the Promise that the then returned.

  • If a suitable handler isn't defined for the Promise then its state is passed on to the next Promise in the chain in lieu of the state that would have been determined by the missing handler.

  • If a handler doesn't throw an exception then the fulfilled state is passed on to the next Promise in the chain. That is, if the handler doesn't return a Promise then as long as it hasn't thrown an exception the next Promise is fulfilled.

  • If a handler throws an exception then the next Promise in the chain is rejected.

  • The catch function can be used to define an onError handler – it is equivalent to then(null,onError).

 

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

Also by Ian Elliot 
Just JavaScript: An Idiomatic Approach
Just jQuery: The Core UI 
Just jQuery: Events, Async & AJAX  

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info

<ASIN:1871962560>

<ASIN:1871962579>

 <ASIN:1871962528>

<ASIN:1871962501>



Last Updated ( Monday, 01 October 2018 )