JavaScript Async - Consuming Promises |
Written by Ian Elliot | |||||
Monday, 01 October 2018 | |||||
Page 4 of 4
Promise Error HandlingOne 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 ChainWe 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
Now Available as a Book:JavaScript AsyncYou can buy it from: Amazon Contents
Also by Ian Elliot 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.
Comments
or email your comment to: comments@i-programmer.info <ASIN:1871962560> <ASIN:1871962579> <ASIN:1871962528> <ASIN:1871962501> |
|||||
Last Updated ( Monday, 01 October 2018 ) |