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. Is this true? There are also some async calls I know of for sure inside 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.
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. It isn't caught because the exception doesn't happen when you call 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. 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. 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. 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(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. 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. For this example, I'm using jQuery's Deferred
object, but any promise library has similar usage:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With