The Promise
chaining is generic enough to not have 'which step failed' kind of information included out of the box. You could potentially try to decode it from stack trace, but in my opinion that's way more work than its worth. Here are few options you can employ to determine which step failed.
Option 1
Set an extra property (as indicator) on the error object, which could be decoded in the catch block to determine which step in chain, the error originated from.
function predictibleErrors(promise, errorCode) {
return Promise.resolve(promise)
.catch(err => {
const newErr = new Error(err.message);
newErr.origErr = err;
newErr.errorCode = errorCode;
throw newErr;
});
}
Promise.resolve(groupId)
.then(sid => predictibleErrors(getGuestGroup2(sid), 'STEP_1')) // matching carts
.then(group => predictibleErrors(getProducts(group), 'STEP_2')) // add products
.then(result2 => predictibleErrors(getPrices(result2), 'STEP_3'))
.catch(error => { // (**)
console.log('Error on GET cart.js: '+error);
// Check error.errorCode here to know which step failed.
res.sendStatus(500);
});
Option 2
Good old, catch after every step, and re-throw to skip subsequent steps.
Promise.resolve(groupId)
.then(sid => getGuestGroup2(sid)) // matching carts
.catch(err => {
console.log('step 1 failed');
err.handled = true; // Assuming err wasn't a primitive type.
throw err;
})
.then(group => getProducts(group)) // add products
.catch(err => {
if(err.handled) { return; }
console.log('step 2 failed');
err.handled = true;
throw err;
})
.then(result2 => getPrices(result2))
.catch(err => {
if(err.handled) { return; }
console.log('step 3 failed');
err.handled = true;
throw err;
})
.catch(error => {
// Some step has failed before. We don't know about it here,
// but requisite processing for each failure could have been done
// in one of the previous catch blocks.
console.log('Error on GET cart.js: '+error);
res.sendStatus(500);
});
Please note that what we do in option two here, could also be done by the underlying methods directly. For eg. getGuestGroup2
or getProducts
could include an errorCode
or similar property on the error object that it throws. In this example we know step 1 or step 2 failed, but cannot know why. If the underlying methods were setting the same, they could include more accurate errorCodes with the knowledge of why the operation failed. I didn't take that route since the methods are not included in your sample and as far as I know they might not be in your control.