stop promise chain with multiple catches

bluebird, node.js, promise

Solution

The simplest solution by far is to use what I call "insulated catches". ie, a pattern in which each `.catch()` is a specialist, associated with a particular step in the overall process, and the main chain comprises only .thens (and eventually a single, terminal catch).

Also, it is useful in this kind of circumstance to convey added information down the error path by re-throwing Error objects with added properties. This avoids the need for custom Errors.

Promise.resolve()
.then(function() {
    return readFile(false)
    .then(function (fileContents) {
        console.log('Successfully read the file:', fileContents);
        return fileContents;
    })
    .catch(function (error) {
        error.code = 521; // or whatever
        error.customMessage = 'Failed to read the file';
        throw error;
    })
})
.then(function (fileContents) {
    return validate(fileContents, false)
    .then(function (result) {
        console.log('Successfully validated the file:', result);
        return fileContents;
    })
    .catch(function (error) {
        error.code = 522; // or whatever
        error.customMessage = 'Failed to validate the file';
        throw error;
    });
})
.catch(function(error) { // terminal catch.
    console.log(error); 
    // It's possible for unaugmented errors to reach this point,
    // so be sure to test for the extra properties before trying to use them.
    if(error.code) {...}
    if(error.customMessage) {...}
    // Note also that the original error.message is still intact.
});

The initial `Promise.resolve()` isn't strictly necessary, but helps keep everything else symetrical.

This will work with any Promises/A+ lib. Bluebird-sugar is not required.

Problem

In Node.js, I need to read a file and validate it's contents, all in async. I m using Node.js 6.6, bluebird 3.4.6 Example code: ``` // pseudo function to read file contents - resolves when 'flag' is true, rejects when 'flag' is false. function readFile(flag) { return new Promise(function (resolve, reject) { console.log('Reading file...'); if (flag) { resolve('File contents'); } else { reject('readFile error'); } }); } // pseudo function to validate file contents - resolves when 'flag' is true, rejects when 'flag' is false. function validate(fileContents, flag) { return new Promise(function (resolve, reject) { console.log('Validating file: ', fileContents); if (flag) { resolve('Validate passed'); } else { reject('validation failed'); } }); } readFile(false) .then(function (fileContents) { console.log('Successfully read the file:', fileContents); return fileContents; }) .catch(function (fileReadErr) { console.log('Failed to read the file:', fileReadErr); throw fileReadErr; // or, return Promise.reject(err); }) .then(function (fileContents) { return validate(fileContents, false); }) .then(function (result) { console.log('Successfully validated the file:', result); }) .catch(function (err) { console.log('Failed to validate the file:', err); }) ; ``` ``` <script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script> ``` The above code will print ``` Reading file... Failed to read the file: readFile error Failed to validate the file: readFile error ``` The above promise chain roughly translates to below sync code: ``` try { let fileContents; try { fileContents = readFile(false); console.log('Successfully read the file:', fileContents); } catch (e) { console.log('Failed to read the file:', e); throw e; } let validationResult = validate(fileContents, false); console.log('Successfully validated the file:', validationResult); } catch (err) { console.log('Failed to validate the file:', err); } ``` And, throwing or rejecting in the first `catch` method will still invoke the 2nd `catch` method. My question: Is there any way to break the chain once the file reading is failed? My objective is to return different HTTP status codes (file read error: 500, validation failed: 400) from an express.js route. I know a solution using non-standard specialized `catch` method, but that requires special handling. In the sense, I need to throw errors or need some filtering key in the error object and both of which are not in my hands, and involves some work to achieve it. This solution is mentioned in bluebird docs & here: Handling multiple catches in promise chain

Original source

Related problems