快速提问,如果我这样做:
setInterval(function() {
try {
riskyFunc();
} catch(e){
console.log(e);
}
}, 1000);
我在想,如果
riskyFunc()
出了什么问题,就会被抓住。这是真的?我确信里面还有一些异步调用 riskyFunc()
。
是的,它会被捕获:但只有在执行回调时才会被捕获。也就是说,如果
riskyFunc
抛出异常,则在回调在一秒内执行之前,它不会被您的示例捕获。
您之前可能听说过,在使用异步方法时必须小心异常,而人们通常犯的错误是:
try {
setInterval(function() {
riskyFunc();
}, 1000);
} catch(e) {
console.error(e);
}
当
riskyFunc
抛出异常但未被捕获时,他们会感到困惑。它没有被捕获,因为当你调用 setInterval
时,异常不会发生;当 setInterval
在将来的某个时候调用匿名函数时,就会发生这种情况,而该函数位于原始 try/catch 块的上下文之外。您正在以正确的方式执行此操作:通过在回调内执行异常处理。
如果
riskyFunc
依次调用异步调用,它们也必须以这种方式处理异常。例如:
function riskyFunc() {
// do some stuff
asyncFn(function(){
throw new Error('argh');
}
}
该异常不会被
setInterval
调用内的 try/catch 块捕获。你必须继续应用向下的图案:
function riskyFunc() {
// do some stuff
asyncFn(function() {
try {
// work that may throw exception
} catch(e) {
console.error(e);
}
}
}
如果您希望异常“向上传播”,则必须使用 Promise 或其他方式来指示成功/失败。这是一种常见的方法,通过使用能够报告错误的“完成”回调:
function riskyFunc(done) {
// do some stuff
asyncFn(function() {
try {
// do some more risky work
done(null, 'all done!');
} catch(e) {
done(e);
}
}
}
然后您可以在
setTimeout
中调用它并考虑可能的异步失败:
setTimeout(function() {
try {
riskyFunc(function(err, msg) {
// this will cover any asynchronous errors generated by
// riskyFunc
if(err) return console.error(err);
console.log(msg);
});
} catch(e) {
// riskyFunc threw an exception (not something it
// invoked asynchronously)
console.error(e);
}
}
setInterval
已经将该块放入异步块中。并且无法在不同步的情况下捕获异常。正确的模式是使用 Promise 对象,并在出现问题时调用 fail()
操作。对于这个例子,我使用 jQuery 的 Deferred
对象,但任何 Promise 库都有类似的用法:
var promise = $.Deferred();
setInterval(function () {
try{
riskyFunc();
} catch (e) {
promise.reject(e);
}
promise.resolve(/* some data */);
}, 1000);
promise
.done(function (data) { /* Handled resolve data */ })
.fail(function (error) { /* Handle error */ });
请注意,由于您使用的是
setInterval
而不是 setTimeout
,除非您清除超时,否则每秒都会调用该函数,因此如果您需要并行多次调用该函数,您可能需要一组 Promise。
如果riskyFunc是
function() {
process.nextTick(function() {
throw "mistake";
});
}
你的catch块不会被捕获。我相信这就是您所担心的情况,您所能做的就是设置全局异常处理程序,或者祈祷最好的结果。 (不,承诺不会捕捉到这一点。它们不是魔法。)
感谢@Ethan Brown 的详细解释。我认为你最后的 setTimeout 缺少计时器 - 见下文
setTimeout(function() {
try {
riskyFunc(function(err, msg) {
// this will cover any asynchronous errors generated by
// riskyFunc
if(err) return console.error(err);
console.log(msg);
});
} catch(e) {
// riskyFunc threw an exception (not something it
// invoked asynchronously)
console.error(e);
}
}, 1000)
我通过使用promise.race和promise找到了一种方法。不确定这种正确方法,但将有助于捕获 catch 块中的错误,而无需在 setInterval 中专门添加 try 和 catch
try{
await Promise.race([
new Promise((resolve, reject) => {
setIntervalObj = setInterval(() => {
reject('Threshold memory exceeded')
}
}, 1000);
})
]);
}
catch(err){
console.log(err);
//do something
}