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 riskyFunc is
function() {
process.nextTick(function() {
throw "mistake";
});
}
your catch block will not 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. I think your last setTimeout is missing the timer - see below
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)
Related
I'm trying to exit the function if an error occurs inside the try block, and still run cleanup code in finally.
I'm doing it by using shouldContinue that is set to true initially, and set it to false inside the catch block if the execution shouldn't continue.
async uploadToServer() {
let response;
let shouldContinue = true;
try {
response = await this.uploderService.uploadFileToServer();
} catch (error) {
this.displayUploadError(error);
shouldContinue = false;
} finally {
// run cleanup code anyway
this.resetSession();
}
if (!shouldContinue) {
return;
}
this.saveResponse(response);
// continue execution here
// ...
}
Is there a better way to exit the function after an error occurs inside the try block and still run code in finally?
One way (probably my personal preferred way) is to simply put everything inside the try (if possible - e.g. if nothing else inside the try could throw an unrelated error):
async uploadToServer() {
try {
const response = await this.uploderService.uploadFileToServer();
this.saveResponse(response);
// continue execution here
// ...
} catch (error) {
this.displayUploadError(error);
} finally {
// run cleanup code anyway
this.resetSession();
}
}
Another way is to return in the catch (finally still runs):
async uploadToServer() {
let response;
try {
response = await this.uploderService.uploadFileToServer();
} catch (error) {
this.displayUploadError(error);
return;
} finally {
// run cleanup code anyway
this.resetSession();
}
this.saveResponse(response);
// continue execution here
// ...
}
Finally will be executed anyway, either the try executed or catch.
Check the description in the link below for more information.
Mozilla developer reference
This question already has an answer here:
Using Q.promises: how to catch an async throw?
(1 answer)
Closed 6 years ago.
I know that stackoverflow is full of similar question and I've read a lot of them.
From what I got a throw inside a promise should reject it, as I can read in the documentation:
If the executor throws an exception, its value will be passed to the reject resolving function.
But even after read a lot of post about promises and throw I still don't understand the snippet of code I'm pasting and why it happens.
function foo(a, b, cb) {
setTimeout(() => {
cb('Inner error *!?"$%&##"');
}, 0);
}
const getThePromise = () => {
return new Promise((resolve, reject) => {
const cb = (err) => {
/* >>> ************ */
throw err; // catch not called
// reject(err); // catch called
/* ************ <<< */
}
foo('foo', 'dudee', cb);
});
}
getThePromise()
.catch((err) => {
console.log('CATCH:', err);
})
.then((res) => {
console.log('then...');
})
I don't understand why if I use the throw the .catch of the promise is not called but if I use the reject it is called.
Just for sake of clarification I'm using Node.js v6.2.2 in a Mac OS/X 10.11 but I don't think it could be also a browser issue.
You are throwing your error inside an asynchronous setTimeout call, which will lead to an uncaught error. The asynchronous code will not execute in the same context as the try-catch block. This has nothing to do with the promise API. This is just part of the behavior of asynchronous code execution in JavaScript.
Take a look at the following example.
const asyncOperation = err => {
try {
setTimeout(function() {
throw err; // will be dropped onto the event queue
// until the call stack is empty
// even if this takes longer than
// a second.
}, 1000);
} catch (e) {
console.log(e) // will not be called
}
}
asyncOperation('Inner error *!?"$%&##"')
And now the same example with the try-catch block inside the setTimeout call and the error being thrown inside the try block.
const asyncOperation = err => {
setTimeout(function() {
try {
throw err // here the error will be throw inside
} catch (e) { // the try block and has the same execution
console.log(e) // context.
}
}, 1000);
}
asyncOperation('Inner error *!?"$%&##"')
You can find more information regarding the Promise.catch right here.
Promise.prototype.catch()
The catch() method returns a Promise and deals with rejected cases only.
There is actually an example with the same situation you are describing in your example. Check out
Gotchas when throwing errors
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // This is never called
});
I have some code that handles Angular promises like this
somethingThatReturnsAPromise
.then(function (data) {
// handle success
})
.catch(function (error) {
// handle error
})
.finally(function () {
// always do this
});
I understand this syntax is deprecated now, and this code should be replaced with
somethingThatReturnsAPromise.then(
function (data) {
// handle success
},
function (error) {
// handle error
}
);
But where should I put the code that was previously in finally when using this new syntax, i.e. code that is executed both when the promise is resolved (successful) and rejected (fails)?
If you want to go with then either way, you can provide a handler for both success and error promise handlers:
function always() {
// Do whatever either it fails or succeeds
}
somethingThatReturnsAPromise.then(always, always).then(function(data) {
}, function(error) {
});
1st: I didn't find anything about any (Promise-related) method being deprecated in the official docs.
2nd: finally is way more complicated then a then(cb, cb), since it doesn't catch Errors, and doesn't propagate the results of your callback, but if you return a promise, it waits for this promise to resolve until it continues propagating the current value.
Sth like this:
function _finally(promise, callback) {
var handleValue = isError => value => $q((resolve, reject) => {
//call your callback
//if this throws, propagate the Error
var w = typeof callback === "function" && callback();
//prepare to push the current value/error
var fn = isError?
() => reject(value):
() => resolve(value);
//check wether your callback has returned sth. Promise-like
if(w && typeof w.then === "function"){
//then we'll wait for this to resolve,
//before we continue propagating the current value/error
w.then(fn, fn);
}else{
//otherwise propagate the current value/error emmediately
fn();
}
});
return $q.resolve(promise).then(
handleValue(false),
handleValue(true)
);
}
I wrote this code only to give you an implession what finally does. Angular's implementation is smoother, so stick with that.
I don't see any reason to think that catch or finally are deprecated or will ever be.
I want to unit test a function.
In that function I'm using Co with a generator function.
When an error occurs I catch it, and I call cb with the error
In my unit test I make a false assertion but mocha doesn't report it, it just times out:
//function to test
function start(data, cb) {
co(function * coStart() {
yield Promise.reject('err'); // yield error for sake of demo
}).then(function(result){
return cb(null, result);
}).catch(function (err) {
// we get here
return cb(err);
});
}
// mocha test
it('fails on incorrect data', function(done){
MyModel.start({'foo': 'bar'}, function(err, res){
assert.equal(err, 'err2'); //this fails but mocha just stops here and times out
done();
});
});
Clearly I'm doing something wrong here?
I know you can return a promise to mocha and omit the done-callback in the test, but my function 'start' cannot return a promise, its like middleware so it should work with a callback
Your code does something similar to this:
Promise.reject('err')
.catch(() => {
// throw another exception
throw 'foo';
});
That is: within the .catch() clause, another exception is thrown (in your case, the exception thrown by assert.equal(err, 'err2')), synchronously, which isn't handled (by, for instance, another .catch() clause). This will cause the second exception to be ignored (see this answer for an explanation), but it will stop the test case from finishing (the done() line is never reached, hence timing out the test case).
If you really need callback support, you can work around this by either adding another .catch() clause in start(), or by calling the callbacks asynchronously:
return setImmediate(function() { cb(null, result) });
...
return setImmediate(function() { cb(err) });
...
But ideally, you should consider the possibility of removing callback support entirely and just passing around the promises. Mocha supports promises out of the box, so the code would look something like this:
function start(data) {
return co(function * coStart() {
yield Promise.reject('err');
});
}
it('fails on incorrect data', function(){
return start({'foo': 'bar'}).then(function() {
throw new Error('should not be reached');
}, function(err) {
assert.equal(err, 'err2');
});
});
I have some code that I want to refactor (extract server communication methods from controller to separate service).
Example:
$http.post("/mypath", someData)
.success(function(request) {
if (request.ok) {
$scope.error = "";
_refreshAppointments();
}
else {
$scope.error = request.err;
}
})
.error(function() {
$scope.error = "Error during communicating to server";
});
My current problem is errors processing (communication with old $scope). So I want to throw the exceptions instead such lines $scope.error = "Error during communicating to server";
And catch them in controller.
Is it good idea?
If you throw an error in a vanilla environment:
setTimeout(function () {
throw new Error();
}, 1);
The error just gets lost. (window.onerror will see it though)
Even if you do:
try {
setTimeout(function () {
throw new Error();
}, 1);
} catch (e) {
console.log(e);
}
You wont see the error.
You need to wrap each asynchronous event, like:
function mySetTimeout(callback, ms) {
setTimeout(wrap_in_try_catch(callback), ms);
}
mySetTimeout(function () {
throw new Error();
});
You can now catch the error in a generic error handler, but you still can't catch it in the surrounding code.
This is where Promises come in. Not all libraries do Promises the same way (or correctly) so I don't know how good your library support is.
Roughly your code will look like:
$.ajax().then(function () {
throw new Error();
}).fail(e) {
console.log("it failed!", e);
});
If instead you have:
$.ajax().then(function () {
throw new Error();
}); // throws something like: no fail callback for rejected value error
Then your global error handler will pick it up. This ensures no error can slip through the cracks and get lost.
Getting a Promise library to work with Errors in this way is not impossible but it's a little bit tricky to set up. Once you have this though, you're good. Error handling becomes a breeze.
You'll never write a try-catch again, just a bunch of .fail() handlers.
It's definitely good idea to extract REST/http requests into model/service layer and use those services from controller. Then handling failed operation would mean rejecting a corresponding promise, in this case throwing exception in promise effectively means the same.
For example this is how your service/factory could look like:
app.factory('dataService', function($http) {
return {
load: function() {
return $http.post("/mypath", someData).then(function(response) {
if (!response.data.ok) {
return throw new Error(response.request.err);
// or return $q.reject(response.request.err);
}
return response.request;
});
}
};
});
and consuming controller would deal with promise status, resolved (success) or rejected (failed/exception):
dataService.load().then(function(request) {
$scope.error = "";
_refreshAppointments();
})
.catch(function(err) {
$scope.error = err || "Error during communicating to server";
});