停止承诺链多个捕获

在Node.js中,我需要读取一个文件并validation它的内容,所有内容都是asynchronous的。 我使用Node.js 6.6蓝鸟3.4.6

示例代码:

// 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> 

上面的代码将打印

 Reading file... Failed to read the file: readFile error Failed to validate the file: readFile error 

上面的promise链粗略地转换为下面的同步代码:

 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); } 

而且,在第一个catch方法中抛出或者拒绝仍然会调用第二个catch方法

我的问题 :一旦文件阅读失败,有没有办法打破这个链条? 我的目标是从express.js路由返回不同的HTTP状态代码(文件读取错误:500,validation失败:400)。

我知道一个解决scheme,使用非标准的专业catch方法,但需要特殊处理。 从某种意义上讲,我需要抛出错误或者在错误对象中需要一些过滤关键字,而这两个关键字都不在我手中,并且需要一些工作来实现它。 这个解决scheme在bluebird docs&here中提到: 处理promise链中的多个catch

到目前为止,最简单的解决scheme是使用我所谓的“绝缘捕捉”。 即每个.catch()是一个专家,与整个过程中的一个特定步骤相关的模式,而主链只包含.thens(最终是一个单一的terminal捕获)。

此外,在这种情况下,通过重新抛出具有添加属性的错误对象,可以在错误path中传递附加信息。 这避免了自定义错误的需要。

 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. }); 

最初的Promise.resolve()不是绝对必要的,但有助于保持其他所有对象。

这将与任何Promises / A + lib一起工作。 蓝鸟糖不是必需的。

你可以像这样创build自定义的错误types:

 ReadFileError = function() {}; ReadFileError.prototype = Error.prototype; ValidationError = function() {}; ValidationError.prototype = Error.prototype; 

那么,你可以throw一个承诺,而不是拒绝:

 function validate(fileContents, flag) { return new Promise(function (resolve, reject) { console.log('Validating file: ', fileContents); if (flag) { resolve('Validate passed'); } else { throw new ReadFileError('readFile error'); } }); } 

那么你可以根据它们的types捕捉到不同的错误:

 readFile(false) .then(function (fileContents) { console.log('Successfully read the file:', fileContents); return fileContents; }) .then(function (fileContents) { return validate(fileContents, false); }) .then(function (result) { console.log('Successfully validated the file:', result); }) .catch(ReadFileError, function (err) { console.log(..., err); }) .catch(ValidationError, function (err) { console.log(..., err); }) catch(function(err) { ... }); 

据我所知,你想达到什么目的,我会build议总是使用一个单一的catch块(当可以避免引入嵌套承诺的逻辑,这是完全可以在几个用例,但应该避免,因为你会有可能结束与地狱的承诺到处都是)

你能处理你的函数readFile所有错误,以统一的方式validate ,如:

 const error = new Error('something bad happened') error.status = 500 return reject(error) 

那么你可以在一个单独的catch块中根据status处理错误逻辑,例如res.status(err.status || 500).json(...)