在setInterval中捕获异常

问题描述 投票:0回答:5

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

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

我在想,如果

riskyFunc()
出了什么问题,就会被抓住。这是真的?我确信里面还有一些异步调用
riskyFunc()

javascript node.js
5个回答
32
投票

是的,它会被捕获:但只有在执行回调时才会被捕获。也就是说,如果

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);
    }
}

4
投票

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。


3
投票

如果riskyFunc是

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

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


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)

0
投票

我通过使用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
}
© www.soinside.com 2019 - 2024. All rights reserved.