jQuery 3 - Consuming Promises
Written by Ian Elliot   
Thursday, 06 July 2017
Article Index
jQuery 3 - Consuming Promises
Chaining Promises
Combining Promises
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 in a function while other functions are still executing. There is generally no way of cancelling 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. 

$.get("TextFile1.txt")
 .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 a chaining thens. 

For example:

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

where chaining has been avoided to show the explicit promises returned. The first then returns promise2 that is settled when onSucess1 returns a value or a settled promise. 

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

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

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

However there is an additional rule governing chained promises. If there is no onSucess or onFailure to handle the settlement of the promise then that state is passed to the next promise and so on until there is 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(onSucess2,onFailure2);

and promise1 is fulfilled there is no onSucess handler defined in its then. What happens is that this state is passed to promise2 which is fulfilled and onSucess2 is executed. Notice that the final state of promise2 would have been determined by the missing onSucess1 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(onSucess1);
var promise3=promise2.then(onSucess2,onFailure2);
var promise4=promise3.then(onSucess3);

which would normally be written:

promise1.then(onSucess1)
        .then(onSucess2,onFailure2)
        .then(onSucess3);

Suppose promise1 is rejected then 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 then promise3 is fulfilled and onSucess3 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(onSucess1);
var promise3=promise2.then(onSucess2);
var promise4=promise3.then(onSucess3);
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 in 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(onSucess1);
var promise3=promise2.then(onSucess2);
var promise4=promise3.then(onSucess3);
var promise5=promise4.catch(onFailure4);

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

promise1.then(onSucess1)
        .then(onSucess2)
        .then(onSucess3)
        .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 onSucess2 then effectively control jumps straight to onFailure4. 

 

jquery2cover

Conclusion

There are lots and lots of situations that you can use promises in that we haven't looked at. A general programming feature like promises are capable of very varied use and a full catalogue is very likely impossible. However if you understand how they work then you should be able to work out how promises behave in any given situation. 

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. 

  • You can convert a jQuery promise to a JavaScript promise using:
    var javaScriptPromise=Promise.resolve(jQueryPromise);
    However this isn't usually necessary as jQuery promises are standards compatible.

  • 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 then this in turn 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 its 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). 

  

Just jQuery
Events, Async & AJAX

Is now available as a print book: Amazon 

jquery2cover

 

Contents

  1. Events, Async & Ajax (Book Only)
  2. Reinventing Events
  3. Working With Events
  4. Asynchronous Code
  5. Consuming Promises
  6. Using Promises 
  7. WebWorkers
  8. Ajax the Basics - get
  9. Ajax the Basics -  post
  10. Ajax - Advanced Ajax To The Server
  11. Ajax - Advanced Ajax To The Client
  12. Ajax - Advanced Ajax Transports And JSONP
  13. Ajax - Advanced Ajax The jsXHR Object
  14. Ajax - Advanced Ajax Character Coding And Encoding 

Also Available:

buy from Amazon

smallcoverjQuery

 

Advanced Attributes

 

Banner


JavaScript Canvas - Fetch API

Working with lower-level data is very much part of graphics. This extract from Ian Elliot's book on JavaScript Graphics looks at how to use typed arrays to access graphic data.



JavaScript Jems - The Inheritance Tax

JavaScript should not be judged as if it was a poor version of the other popular languages - it isn't a Java or a C++ clone. It does things its own way.  In particular, it doesn't do inheritance  [ ... ]


Other Articles

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

 



Last Updated ( Thursday, 05 May 2022 )