简体   繁体   English

在 setInterval 中捕获异常

[英]Catching exceptions in setInterval

Quick question, if I do this:快速提问,如果我这样做:

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

In my head I am thinking that if anything goes wrong in riskyFunc() , it will be caught.在我的脑海中,我在想,如果riskyFunc()出现任何问题,就会被抓住。 Is this true?这是真的? There are also some async calls I know of for sure inside riskyFunc() .riskyFunc()中还有一些我知道的异步调用。

Yes, it will be caught: but only when the callback is executed.是的,它会被捕获:但只有在执行回调时。 That is, if riskyFunc throws an exception, it won't be caught in your example until the callback executes in one second.也就是说,如果riskyFunc抛出异常,它不会在您的示例中被捕获,直到回调在一秒钟内执行。

You've probably heard before that you have to be careful with exceptions when using asynchronous methods, and the usual mistake people make is this:您之前可能听说过,在使用异步方法时必须小心处理异常,人们常犯的错误是:

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

They are confused when riskyFunc throws an exception and it isn't caught.riskyFunc抛出异常并且没有被捕获时,他们会感到困惑。 It isn't caught because the exception doesn't happen when you call setInterval ;它没有被捕获,因为调用setInterval时不会发生异常; it happens when setInterval invokes the anonymous function sometime in the future, which is outside of the context of the original try/catch block.setInterval在未来某个时候调用匿名函数时会发生这种情况,这在原始 try/catch 块的上下文之外。 You are doing it the correct way: by doing the exception handling inside the callback.您正在以正确的方式进行操作:通过在回调中进行异常处理。

If riskyFunc in turn invokes asynchronous calls, those too have to handle exceptions in this manner.如果riskyFunc反过来调用异步调用,那些也必须以这种方式处理异常。 For example:例如:

function riskyFunc() {
    // do some stuff
    asyncFn(function(){
        throw new Error('argh');
    }
}

That exception will not get caught in the try/catch block inside your setInterval call.该异常不会在setInterval调用中的 try/catch 块中捕获。 You'll have to keep applying the pattern on down:您必须继续向下应用该模式:

function riskyFunc() {
    // do some stuff
    asyncFn(function() {
        try {
            // work that may throw exception
        } catch(e) {
            console.error(e);
        }
    }
}

If you want the exception to "propagate up", you'll have to use promises, or some other way to indicate success/failure.如果您希望异常“传播”,则必须使用承诺或其他方式来指示成功/失败。 Here's a common method, by using a "done" callback that is capable of reporting an error:这是一种常用方法,通过使用能够报告错误的“完成”回调:

function riskyFunc(done) {
    // do some stuff
    asyncFn(function() {
        try {
            // do some more risky work
            done(null, 'all done!');
        } catch(e) {
            done(e);
        }
    }
}

Then you can call that in your setTimeout and take into account possible asynchronous failures:然后您可以在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 already puts the block in an asynchronous block. setInterval已经将块放入异步块中。 And exceptions can't be caught in out-of-sync.并且不能在不同步的情况下捕获异常。 The right pattern is to use a Promise object, and call a fail() operation if something goes wrong.正确的模式是使用 Promise 对象,并在fail()调用fail()操作。 For this example, I'm using jQuery's Deferred object, but any promise library has similar usage:在这个例子中,我使用了 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 */ });

Note that since you're using setInterval instead of setTimeout that this will be called every second unless you clear the timeout, so if you need to call the function multiple times in parallel, you might want an array of promises.请注意,由于您使用的是setInterval而不是setTimeout除非您清除超时,否则它将每秒调用一次,因此如果您需要并行多次调用该函数,您可能需要一个承诺数组。

If riskyFunc is如果 riskyFunc 是

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

your catch block will not catch.您的 catch 块将无法捕获。 I believe this is the case you are worried about, and all you can do is set global exception handlers, or hope for the best.我相信这就是您所担心的情况,您所能做的就是设置全局异常处理程序,或者希望最好。 (No, promises will not catch this. They are not magic.) (不,承诺不会抓住这一点。它们不是魔术。)

Thanks @Ethan Brown for the detailed explanation.感谢@Ethan Brown 的详细解释。 I think your last setTimeout is missing the timer - see below我认为您的最后一次 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM