Javascript 捕获setInterval中的异常

Javascript 捕获setInterval中的异常,javascript,node.js,Javascript,Node.js,快速提问,如果我这样做: setInterval(function() { try { riskyFunc(); } catch(e){ console.log(e); } }, 1000); 在我的脑海中,我在想,如果在riskyFunc()中出现任何错误,它就会被捕获。这是真的吗?我知道在riskyFunc()中肯定有一些异步调用。是的,它会被捕获:但只有在执行回调时才会被捕获。也就是说,如果riskyFunc抛出异常,则在一秒钟内执

快速提问,如果我这样做:

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);
        }
    }
}
如果希望异常“向上传播”,则必须使用承诺或其他方式来表示成功/失败。下面是一个常用方法,通过使用能够报告错误的“完成”回调:

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);
    }
}
如果riskyFunc是

function() {
    process.nextTick(function() {
        throw "mistake";
    });
}

你的挡块抓不住。我相信这正是您所担心的情况,您所能做的就是设置全局异常处理程序,或者期待最好的结果。(不,承诺不会捕捉到这一点。它们不是魔法。)

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
,因此除非清除超时,否则将每秒调用一次,因此如果需要并行多次调用函数,您可能需要一系列承诺。

感谢@Ethan Brown的详细解释。我认为您的上一次设置超时缺少计时器-请参见下文

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)

如果您觉得这很有用,请您解释一下异常不能在不同步中捕获的含义。