快速提问,如果我这样做:
setInterval(function() {
try {
riskyFunc();
} catch(e){
console.log(e);
}
}, 1000);
在我脑海中,我想如果riskyFunc()
出现任何问题,它就会被抓住。这是真的?我在riskyFunc()
内确实知道了一些异步调用。
答案 0 :(得分:12)
是的,它会被捕获:但只有在执行回调时才会被捕获。也就是说,如果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);
}
}
}
如果您希望将例外传播到"那么,您必须使用承诺或其他方式来表示成功/失败。这是一种常见的方法,通过使用"完成"能够报告错误的回调:
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);
}
}
答案 1 :(得分:3)
如果riskyFunc是
function() {
process.nextTick(function() {
throw "mistake";
});
}
你的捕获块不会捕获。我相信这是你担心的情况,你所能做的就是设置全局异常处理程序,或者希望最好。 (不,承诺不会抓住这个。他们不是魔法。)
答案 2 :(得分:3)
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
,除非您清除超时,否则每秒都会调用它,因此如果您需要多次并行调用该函数,则可能想要一系列承诺。
答案 3 :(得分:0)
感谢@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)