Search code examples
javascriptes6-promise

Are `.then` and `.catch` Promise handlers somehow paired and treated like `.then(resolutionHandler, rejectionHandler)`?


When working with promises in JavaScript, we have a possibility to use .then, .catch and .finally. Each of these methods returns a new Promise object.

Using .then is pretty straightforward - we chain them. The use case for finally - is to put it at the end of the chain of .then and .catch. But

In the code below, as I understand, we initialize promise p which can resolve or reject. I could use .then(resolutionHandler, rejectionHandler), which would be self-explanatory as it's only 1 .then "handler" with both handlers, but in case of sequencing .then and .catch instead of the latter approach-

**Are .then and .catch handlers somehow paired and treated like .then(resolutionHandler, rejectionHandler)? or something else is happening? **

const p = new Promise((resolve, reject) => {
  reject("ups...");
});

p
  .then(data => {
    // this is success handler for Promise "p"
  })
  .catch(err => {
    // Is this failure handler for Promise "p"?
  })

Solution

  • Not exactly. When you have p.then(handleThen).catch(handleCatch), if p rejects, handleCatch will handle it. But handleCatch will also handle errors thrown by handleThen.

    Although those sorts of errors are pretty unusual with if handleThen contains only synchronous code, if handleThen returns a Promise, handleCatch will be able to handle that Promise if it rejects.

    <somePromiseChain>
      .catch(handleCatch);
    

    will have the handleCatch handle any errors thrown anywhere in the above Promise chain.

    In contrast, with p.then(resolutionHandler, rejectionHandler), the rejectionHandler will only handle a rejection of p. It will completely ignore anything that occurs in resolutionHandler.

    If the resolutionHandler is completely synchronous, doesn't return a Promise, and never throws (which is pretty common), then .then(resolutionHandler, rejectionHandler) is indeed equivalent to.then(resolutionHandler).catch(rejectionHandler). But it's usually a good idea to use .then(..).catch(..).

    With p.then(success).catch(fail), the .catch is actually attached to the Promise returned by the .then - but if p rejects, the .then's Promise rejects as well, with the same value as p's rejection. It's not that the catch is attached directly to p, but it's attached to a .then which passes p's rejection through.

    Every .then / .catch is attached directly to the upper Promise it's called on, but sometimes that upper Promise passes through the value from its upper Promise unaltered. That is. p.then(undefined, fail).then(success) will run success if p resolves, with the intermediate .then passing the resolution through. With rejection, p.then(success).catch(fail) will run fail because the .then(success) passes the failure from p through unaltered.