在下面的代码示例中,函数baz()抛出一个TypeError,当在fs.open
回调中的new Promise
回调中调用时,节点进程立即以非零值退出,异常是从未被抓住过。
var Promise = require('bluebird');
var fs = require('fs');
function baz() {
[].toDateString(); // should throw type error
}
function bar() {
return new Promise((resolve, reject) => {
fs.open('myfile', 'r', (err, fd) => {
baz(); // throws TypeError [].toDateString which is never caught.
return resolve('done');
});
})
.catch((error) => {
console.log('caught errror');
console.log(error);
return Promise.resolve(error);
});
}
bar()
.then((ret) => {
console.log('done');
});
输出:
$ node promise_test.js
/Users/.../promise_test.js:5
[].toDateString(); // should throw type error
^
TypeError: [].toDateString is not a function
at baz (/Users/..../promise_test.js:5:6)
at fs.open (/Users/..../promise_test.js:12:7)
at FSReqWrap.oncomplete (fs.js:123:15)
✘-1 ~/
如果我稍微修改此代码以在promise回调中抛出异常但在fs.open
回调之外,异常将按预期捕获并继续执行:
return new Promise((resolve, reject) => {
baz(); // throws TypeError [].toDateString
fs.open('myfile', 'r', (err, fd) => {
console.log(err);
return resolve('done');
});
输出:
$ node promise_test.js
caught errror
TypeError: [].toDateString is not a function
...
done
答案 0 :(得分:3)
因为异常发生在fs.open()
异步回调中,所以异常会回到fs.open()
中调用完成回调的异步事件处理程序,然后它就会消失,并且没有机会在任何地方传播。蓝鸟从来没有机会看到它。
这是一个教科书示例,说明为什么不应将常规异步回调代码与promise代码混合使用。相反,promisify fs.open()
并使用promisified版本,然后承诺基础设施将适当地捕获异常。
fs.openAsync = function(fname, mode) {
return new Promise(function(resolve, reject) {
fs.open(fname, mode, function(err, fd) {
if (err) return reject(err);
resolve(fd);
});
});
}
function bar() {
return fs.openAsync('myfile', 'r').then(function(fd) {
baz(); // throws TypeError [].toDateString which will now be caught
// in the .catch() below
}).catch(function(err) {
// handle error here
});
}
或者,在Bluebird中,您可以使用内置的promisify函数:
const fs = Promise.promisifyAll(require('fs'));
这会自动创建fs.openAsync()
,它会返回所有其他异步方法的promise和promisified版本。
仅供参考,承诺基础设施只能捕获由承诺基础设施本身调用的回调中的异常。它通过将调用包装在它自己的try / catch中来实现。正如您在代码中看到的那样,您正在直接使用fs.open()
回调,这种回调没有机会被包含在这样的try / catch处理程序中以捕获异常并将其转换为拒绝。所以,通常的解决方案是创建一个fs.open()
的promisified版本,它立即拒绝或解析回调,然后你的自定义回调代码进入回调被包装的.then()
处理程序,异常将被抓住并自动变成拒绝。
答案 1 :(得分:0)
本文似乎为吞噬异常的承诺提供了一些指导,以及BlueBird如何帮助处理它们:
Promise.onPossiblyUnhandledRejection(function(error){
throw error;
});
奇怪的是,你确实想放弃拒绝,只需处理 它有一个空的捕获,像这样:
Promise.reject('错误值')。catch(function(){});
http://jamesknelson.com/are-es6-promises-swallowing-your-errors/