停止承诺链多个捕获
在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(...)