skyboyer
skyboyer

Reputation: 23705

how to differentiate error's source based on stage in Promise chain

I have 2 callback that both call API and return Promise. They should go sequentially. Let's name them verifyThing and updateThing.

So without error handling it would be as easy as

verifyThing()
 .then((id) => updateThing(id))

But now error handling comes. Suppose I need to display different error message once verifyThing fails or updateThing. Also obviously I don't need to call updateThing if verifyThing fails.

So far I've tried with custom Error:

class VerifyError extends Error {};
verifyThing()
  .catch(e => {
    message("cannot verify");
    throw new VerifyError();
  })
 .then((id) => updateThing(id))
 .catch(e => {
   if (e instanceof VerifyError) return;
   message("cannot update");
 })

Unfortunately custom Error check does not work with Babel 6.26 we use. As a dirty patch I can throw magic string instead of Error subclass, but ESLint rules are for a reason, right?

So finally I have got working variant. But I believe it has worse readability(because of nesting):

verifyThing()
 .catch(e => {
    message("cannot verify");
    throw e;
  })
 .then((id) => 
   updateThing(id)
     .catch(e => {
       message("cannot update");
     })
 )

Is there any other way to handle all the logic in the same chain?

Upvotes: 3

Views: 56

Answers (2)

trincot
trincot

Reputation: 350365

If async/await is an option, then:

async function() {
    let id;
    try {
        id = await verifyThing();
        await updateThing(id);
    } catch(e) {
        message(id === undefined ? "cannot verify" : "cannot update");
        throw e;
    }
}

This assumes that when verifyThing() fulfills, it will resolve with a defined value.

Upvotes: 2

Patrick Roberts
Patrick Roberts

Reputation: 51896

It seems like the actual behavior you want can be achieved by reordering your last example:

verifyThing().then(id => 
  updateThing(id).catch(e => {
    message("cannot update");
  })
).catch(e => {
  message("cannot verify");
})

The outer catch() will only catch errors from verifyThing() since errors from updateThing(id) have been handled by the inner catch(). In addition, you're always left with a resolved promise instead of a rejected one, which is appropriate since both types of errors have been handled already.

To avoid the appearance that error handling is not close to the source, move the inner portion to a helper function:

function tryUpdateThing (id) {
  return updateThing(id).catch(e => {
    message("cannot update");
  });
}

verifyThing().then(
  tryUpdateThing
).catch(e => {
  message("cannot verify");
});

Whether or not this is acceptably readable, I'll leave up to you to decide.

Upvotes: 2

Related Questions