捕获setInterval中的异常

时间:2014-07-01 16:51:26

标签: javascript node.js

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

setInterval(function() {
    try {
        riskyFunc();
    } catch(e){
        console.log(e);
    }
}, 1000);

在我脑海中,我想如果riskyFunc()出现任何问题,它就会被抓住。这是真的?我在riskyFunc()内确实知道了一些异步调用。

4 个答案:

答案 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)