为什么从 bluebird promise 中的异步函数中抛出的错误没有在 .catch() 函数中捕获?
Why is an error thrown from within an async function inside a bluebird promise not caught in the .catch() function?
在下面的代码示例中,函数 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
这篇文章似乎提供了一些关于承诺吞噬异常的指导,以及 BlueBird 如何帮助处理它们:
Promise.onPossiblyUnhandledRejection(function(error){
throw error;
});
And on the odd chance you do want to discard a rejection, just handle
it with an empty catch, like so:
Promise.reject('error value').catch(function() {});
http://jamesknelson.com/are-es6-promises-swallowing-your-errors/
因为异常发生在 fs.open()
异步回调中,所以异常会返回到 fs.open()
中调用完成回调的异步事件处理程序,然后它会消失并且没有机会传播任何地方。蓝鸟永远没有机会看到它。
这是一个教科书示例,说明为什么不应将常规异步回调代码与 promise 代码混合使用。相反,promisify fs.open()
并使用 promisified 版本,然后异常将被 promise 基础设施适当地捕获。
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()
,其中 returns 所有其他异步方法的承诺和承诺版本。
仅供参考,promise 基础结构只能捕获由 promise 基础结构本身调用的回调中的异常。它通过将对每个回调的调用包装在它自己的 try/catch 中来实现。正如您在代码中看到的,您正在直接使用 fs.open()
回调,它没有这样的机会被包裹在这样的 try/catch 处理程序中以捕获异常并将其变成拒绝。因此,相反,通常的解决方案是创建一个 fs.open()
的 promisified 版本,它立即在回调中拒绝或解决,然后您的自定义回调代码进入 .then()
处理程序,其中回调被包装并且异常将被抓住并自动变成拒绝。
在下面的代码示例中,函数 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
这篇文章似乎提供了一些关于承诺吞噬异常的指导,以及 BlueBird 如何帮助处理它们:
Promise.onPossiblyUnhandledRejection(function(error){
throw error;
});
And on the odd chance you do want to discard a rejection, just handle it with an empty catch, like so:
Promise.reject('error value').catch(function() {});
http://jamesknelson.com/are-es6-promises-swallowing-your-errors/
因为异常发生在 fs.open()
异步回调中,所以异常会返回到 fs.open()
中调用完成回调的异步事件处理程序,然后它会消失并且没有机会传播任何地方。蓝鸟永远没有机会看到它。
这是一个教科书示例,说明为什么不应将常规异步回调代码与 promise 代码混合使用。相反,promisify fs.open()
并使用 promisified 版本,然后异常将被 promise 基础设施适当地捕获。
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()
,其中 returns 所有其他异步方法的承诺和承诺版本。
仅供参考,promise 基础结构只能捕获由 promise 基础结构本身调用的回调中的异常。它通过将对每个回调的调用包装在它自己的 try/catch 中来实现。正如您在代码中看到的,您正在直接使用 fs.open()
回调,它没有这样的机会被包裹在这样的 try/catch 处理程序中以捕获异常并将其变成拒绝。因此,相反,通常的解决方案是创建一个 fs.open()
的 promisified 版本,它立即在回调中拒绝或解决,然后您的自定义回调代码进入 .then()
处理程序,其中回调被包装并且异常将被抓住并自动变成拒绝。