Chai-As-Promised: Handle errors when promise throws error - javascript

I have the following code I need to test:
function A(param){
// Process param
return B(param)
.catch(function(err){
//Process err
throw new customError(err); // throw a custom Error
})
.then(function(response){
// Handle response and return
return {status: 'Success'}
})
}
To test it, I use the following snippet:
return expect(A(param))
.to.eventually.have.property('status', 'Success');
This works fine when the code doesn't break in function A or B, but when it does, the test case fails, and the grunt-mocha fails to exit, which I presume is due to the fact that it still waiting for A to resolve.
If I add a return statement instead of throw in catch block, grunt-mocha exits fine. Is there a better way to test such cases?

Catch is meant to catch and correct any errors, not re-throw them. So returning the error or a rejected promise is the correct way to handle this.

Related

Is there a good way to debug a failing promise?

I had a typo on the .then block of a promise and the promise kept failing. I suppose I didn't realize if there was a type it would go to .catch. It took quite a bit of digging to figure out that was the mistake (kept assuming it was something wrong with the promise/async/etc call.)
Is there a way to get JS to tell me "hey, there's a mistake in your .then block!"
code
searchAPI(name)
.then(data => {
// typo was LowerCase instead of toLowerCase
let filtereddowndata = data
.filter(item =>
item.title.toLowerCase().includes(name.LowerCase())
)
etc etc
})
.catch(function() {
console.log("no match found"); // kept going here.
});
The actual error sent to the .catch() (which you were ignoring in your code) would have given you a good clue why the .catch() was being triggered. Use something like this:
.catch(function(e) {
console.log(e);
// any other processing code here
});
I always make sure to log the actual error in my .catch() statements so I can always see exactly why it got triggered and don't blindly assume anything about how the code got here.
This is also why node.js makes it a console warning (and eventually a run-time error) when you don't have any .catch() because the try/catch built into .then() will hide errors from you if you don't expose them yourself in a .catch().
The above would have been enough to give you the precise error in this case. But there are other situations where (for debugging purposes) you sometimes benefit from inserting your own try/catch statements around more localized areas of your code. That would have also shown you what was happening in your .then() handler.
You can run this snippet to see the actual error.
// simulate your searchAPI() call
function searchAPI() {
return new Promise(resolve => {
resolve([{title: "Superman"}, {title: "Spiderman"}]);
});
}
let name = "Joe";
searchAPI(name).then(data => {
// typo was LowerCase instead of toLowerCase
let filtereddowndata = data.filter(item =>
item.title.toLowerCase().includes(name.LowerCase())
);
}).catch(function(e) {
// console.log(e) will work with normal Javascript
// here in a stackoverflow snippet where console.log has been replaced
// you have to look at console.log(e.message) to see the error
console.log("searchAPI failed - ", e.message);
});
I can only emphasise #jfriend's answer that you should always reject with proper error messages and log them.
However, it is also important to understand how promises get rejected and which .catch() callbacks will handle rejections from where. It's possible to better differentiate error origins by not using the traditional .then(…).catch(…) pattern, and even tag each error with the function it's coming from.
In your case, the error message "no match found" was misplaced as it implies that searchAPI failed, while that's not the only reason for the catch handler to be reached. Instead, the better pattern would be
function searchAPI(name) {
…
return Promise.reject(new Error("No match found"));
// ^^^^^^^^^^^^^^^^ error message (or error code)
} // thrown exactly where the error actually occurred
searchAPI(name).then(data => {
let filtereddowndata = data.filter(item =>
item.title.toLowerCase().includes(name.LowerCase())
)
…
}, err => { // <- second `then` argument
console.log("Error from searchAPI", err);
});
// would now cause an unhandled rejection about the LowerCase method call
You can combine this with a catch handler of course:
searchAPI(name).then(data => {
let filtereddowndata = data.filter(item =>
item.title.toLowerCase().includes(name.LowerCase())
)
…
}, err => { // <- second `then` argument
console.log("Error from searchAPI", err);
}).catch(err => {
console.error("Error from promise callback", err);
});

Throwing an error from a promises catch block fails the test for it being handled

I'm throwing an error from a $httpBackend response to a function and then logging and rethrowing in the catch part of an AngularJS promise chain:
function doAThingThatMustBeLogged(param) {
return ExternalService.thingToLogFunction(param).then(response =>{
MyLogger.Log('All is well');
}).catch(error => {
MyLogger.Log('Oh no.');
throw new Error(error);
})
}
it('should log doAThingThatMustBeLogged regardless of any errors thrown', () =>{
$httpBackend.expectPOST('https://external-service.com/api/thing/to/log').respond(500);
spyOn(MyLogger, 'Log');
doAThingThatMustBeLogged(1)
try {
$httpBackend.flush();
} catch (error) {
// swallow error
}
expect(MyLogger.Log).toHaveBeenCalledWith('Oh no.');
})
Now I am only rethrowing the error as I don't want to swallow it in the non-test code, because the code that calls doAThingThatMustBeLogged can handle it better higher up. However what this means is that I now need to wrap $httpBackend.flush in my test in a try/catch block which then implies an error is thrown.
That implication - that this function throws errors - is not what I want the test to do. I just want to isolate the logging code and test that is called satisfactorially.
Is there a way to run the test above, without having to try // catch around the flush?

Throw error based on result of promise

The below code doesn't handle error though condition is satisfied. Please help understand why
Edit:the below code now terminates code flow in case of satisfying condition but throwing a error result in unhandled rejection.
utils.js
const isMaximum = Id => {
return db.Entries
.findAndCountAll()
.then(counts => {
let numberOfEntries = 2;
let maxEntries = 2;
if (numberOfEntries == maxEntries) {
return true;
}
else{
return false;
}
});
};
xyz.js
const doSomething=(req,res,next)=>{
Utils.isMaximum(id).then(isLimit=> {
if(isLimit) throw new Error("not allowed"); //unhandled rejection
//rest of code gets terminated as expected
});
}
Related questions Why can I not throw inside a Promise.catch handler? couldn't find me a solution. So please make it clear.
Throwing inside a .catch() handler makes the promise chain a rejected promise. If you don't have a subsequent .catch() handler, then it will get reported as an unhandled promise rejction because you have no .catch() afterwards to catch the rejected promise. This is nearly always a programming error which is why you get the warning.
Throwing inside a .catch() does not terminate your program. That exception is caught by the promise infrastructure (and turned into a rejected promise). That's how promises work (by specification). If you want your program to terminate at that point, then you can call process.exit() rather than throwing.
Now you've edited your question and now you're trying to throw inside a .then() handler. OK. I'll address what you've added. Next time please start with the whole code context so we can more quickly understand the situation.
Utils.isMaximum() is asynchronous. It returns a promise. You need to program with using asynchronous techniques. You can't think like synchronous sequential code. Any code that comes after your call to Utils.isMaximum() will execute BEFORE the .then() handler is called.
Here's a simple example:
Utils.isMaximum(id).then(isLimit => {
console.log("A");
});
console.log("B");
That will output:
B
A
So, for starters, you can't prevent the code that comes after the .then() block from running by anything you do inside the .then() block because the rest of the function executes BEFORE the .then() handler does.
So, the usual way you deal with that is you have to put the rest of the code INSIDE the .then() handler or inside a function you call from there:
const doSomething=(req,res,next)=>{
Utils.isMaximum(id).then(isLimit=> {
if(!isLimit){
// rest of code here that sends regular response
} else {
// handle error here - send error response
// or OK to throw here, throw will be caught by next .catch()
}
}).catch(err => {
// send error response here
});
}
I didn't catch the error I throw and that is the only reason why. Below resolved.
const doSomething=(req,res,next)=>{
Utils.isMaximum(id).then(isLimit=> {
if(isLimit) throw new Error("not allowed");
}).catch(next);
//rest of code gets terminated as expected
}

In JavaScript Promises are the reject handler and catch method alternatives [duplicate]

I had a look at the bluebird promise FAQ, in which it mentions that .then(success, fail) is an antipattern. I don't quite understand its explanation as for the try and catch.
What's wrong with the following?
some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
It seems that the example is suggesting the following to be the correct way.
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
What's the difference?
What's the difference?
The .then() call will return a promise that will be rejected in case the callback throws an error. This means, when your success logger fails, the error would be passed to the following .catch() callback, but not to the fail callback that goes alongside success.
Here's a control flow diagram:
To express it in synchronous code:
// some_promise_call().then(logger.log, logger.log)
then: {
try {
var results = some_call();
} catch(e) {
logger.log(e);
break then;
} // else
logger.log(results);
}
The second log (which is like the first argument to .then()) will only be executed in the case that no exception happened. The labelled block and the break statement feel a bit odd, this is actually what python has try-except-else for (recommended reading!).
// some_promise_call().then(logger.log).catch(logger.log)
try {
var results = some_call();
logger.log(results);
} catch(e) {
logger.log(e);
}
The catch logger will also handle exceptions from the success logger call.
So much for the difference.
I don't quite understand its explanation as for the try and catch
The argument is that usually, you want to catch errors in every step of the processing and that you shouldn't use it in chains. The expectation is that you only have one final handler which handles all errors - while, when you use the "antipattern", errors in some of the then-callbacks are not handled.
However, this pattern is actually very useful: When you want to handle errors that happened in exactly this step, and you want to do something entirely different when no error happened - i.e. when the error is unrecoverable. Be aware that this is branching your control flow. Of course, this is sometimes desired.
What's wrong with the following?
some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
That you had to repeat your callback. You rather want
some_promise_call()
.catch(function(e) {
return e; // it's OK, we'll just log it
})
.done(function(res) {
logger.log(res);
});
You also might consider using .finally() for this.
The two aren't quite identical. The difference is that the first example won't catch an exception that's thrown in your success handler. So if your method should only ever return resolved promises, as is often the case, you need a trailing catch handler (or yet another then with an empty success parameter). Sure, it may be that your then handler doesn't do anything that might potentially fail, in which case using one 2-parameter then could be fine.
But I believe the point of the text you linked to is that then is mostly useful versus callbacks in its ability to chain a bunch of asynchronous steps, and when you actually do this, the 2-parameter form of then subtly doesn't behave quite as expected, for the above reason. It's particularly counterintuitive when used mid-chain.
As someone who's done a lot of complex async stuff and bumped into corners like this more than I care to admit, I really recommend avoiding this anti-pattern and going with the separate handler approach.
By looking at advantages and disadvantages of both we can make a calculated guess as to which is appropriate for the situation.
These are the two main approaches to implementing promises. Both have it's pluses and minus
Catch Approach
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
Advantages
All errors are handled by one catch block.
Even catches any exception in the then block.
Chaining of multiple success callbacks
Disadvantages
In case of chaining it becomes difficult to show different error messages.
Success/Error Approach
some_promise_call()
.then(function success(res) { logger.log(res) },
function error(err) { logger.log(err) })
Advantages
You get fine grained error control.
You can have common error handling function for various categories of errors like db error, 500 error etc.
Disavantages
You will still need another catch if you wish to handler errors thrown by the success callback
Simple explain:
In ES2018
When the catch method is called with argument onRejected, the
following steps are taken:
Let promise be the this value.
Return ? Invoke(promise, "then", « undefined, onRejected »).
that means:
promise.then(f1).catch(f2)
equals
promise.then(f1).then(undefiend, f2)
Using .then().catch() lets you enable Promise Chaining which is required to fulfil a workflow. You may need to read some information from database then you want to pass it to an async API then you want to manipulate the response. You may want to push the response back into the database. Handling all these workflows with your concept is doable but very hard to manage. The better solution will be then().then().then().then().catch() which receives all errors in just once catch and lets you keep the maintainability of the code.
Using then() and catch() helps chain success and failure handler on the promise.catch() works on promise returned by then(). It handles,
If promise was rejected. See #3 in the picture
If error occurred in success handler of then(), between line numbers 4 to 7 below. See #2.a in the picture
(Failure callback on then() does not handle this.)
If error occurred in failure handler of then(), line number 8 below. See #3.b in the picture.
1. let promiseRef: Promise = this. aTimetakingTask (false);
2. promiseRef
3. .then(
4. (result) => {
5. /* successfully, resolved promise.
6. Work on data here */
7. },
8. (error) => console.log(error)
9. )
10. .catch( (e) => {
11. /* successfully, resolved promise.
12. Work on data here */
13. });
Note: Many times, failure handler might not be defined if catch() is
written already.
EDIT: reject() result in invoking catch() only if the error
handler in then() is not defined. Notice #3 in the picture to
the catch(). It is invoked when handler in line# 8 and 9 are not
defined.
It makes sense because promise returned by then() does not have an error if a callback is taking care of it.
Instead of words, good example. Following code (if first promise resolved):
Promise.resolve()
.then
(
() => { throw new Error('Error occurs'); },
err => console.log('This error is caught:', err)
);
is identical to:
Promise.resolve()
.catch
(
err => console.log('This error is caught:', err)
)
.then
(
() => { throw new Error('Error occurs'); }
)
But with rejected first promise, this is not identical:
Promise.reject()
.then
(
() => { throw new Error('Error occurs'); },
err => console.log('This error is caught:', err)
);
Promise.reject()
.catch
(
err => console.log('This error is caught:', err)
)
.then
(
() => { throw new Error('Error occurs'); }
)

Why can I not throw inside a Promise.catch handler?

Why can't I just throw an Error inside the catch callback and let the process handle the error as if it were in any other scope?
If I don't do console.log(err) nothing gets printed out and I know nothing about what happened. The process just ends...
Example:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
If callbacks get executed in the main thread, why does the Error get swallowed by a black hole?
As others have explained, the "black hole" is because throwing inside a .catch continues the chain with a rejected promise, and you have no more catches, leading to an unterminated chain, which swallows errors (bad!)
Add one more catch to see what's happening:
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // Where does this go?
}).catch(function(err) {
console.log(err.stack); // It goes here!
});
A catch in the middle of a chain is useful when you want the chain to proceed in spite of a failed step, but a re-throw is useful to continue failing after doing things like logging of information or cleanup steps, perhaps even altering which error is thrown.
Trick
To make the error show up as an error in the web console, as you originally intended, I use this trick:
.catch(function(err) { setTimeout(function() { throw err; }); });
Even the line numbers survive, so the link in web console takes me straight to the file and line where the (original) error happened.
Why it works
Any exception in a function called as a promise fulfillment or rejection handler gets automatically converted to a rejection of the promise you're supposed to return. The promise code that calls your function takes care of this.
A function called by setTimeout on the other hand, always runs from JavaScript stable state, i.e. it runs in a new cycle in the JavaScript event loop. Exceptions there aren't caught by anything, and make it to the web console. Since err holds all the information about the error, including the original stack, file and line number, it still gets reported correctly.
Important things to understand here
Both the then and catch functions return new promise objects.
Either throwing or explicitly rejecting, will move the current promise to the rejected state.
Since then and catch return new promise objects, they can be chained.
If you throw or reject inside a promise handler (then or catch), it will be handled in the next rejection handler down the chaining path.
As mentioned by jfriend00, the then and catch handlers are not executed synchronously. When a handler throws, it will come to an end immediately. So, the stack will be unwound and the exception would be lost. That is why throwing an exception rejects the current promise.
In your case, you are rejecting inside do1 by throwing an Error object. Now, the current promise will be in rejected state and the control will be transferred to the next handler, which is then in our case.
Since the then handler doesn't have a rejection handler, the do2 will not be executed at all. You can confirm this by using console.log inside it. Since the current promise doesn't have a rejection handler, it will also be rejected with the rejection value from the previous promise and the control will be transferred to the next handler which is catch.
As catch is a rejection handler, when you do console.log(err.stack); inside it, you are able to see the error stack trace. Now, you are throwing an Error object from it so the promise returned by catch will also be in rejected state.
Since you have not attached any rejection handler to the catch, you are not able to observe the rejection.
You can split the chain and understand this better, like this
var promise = do1().then(do2);
var promise1 = promise.catch(function (err) {
console.log("Promise", promise);
throw err;
});
promise1.catch(function (err) {
console.log("Promise1", promise1);
});
The output you will get will be something like
Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }
Inside the catch handler 1, you are getting the value of promise object as rejected.
Same way, the promise returned by the catch handler 1, is also rejected with the same error with which the promise was rejected and we are observing it in the second catch handler.
I tried the setTimeout() method detailed above...
.catch(function(err) { setTimeout(function() { throw err; }); });
Annoyingly, I found this to be completely untestable. Because it's throwing an asynchronous error, you can't wrap it inside a try/catch statement, because the catch will have stopped listening by the time error is thrown.
I reverted to just using a listener which worked perfectly and, because it's how JavaScript is meant to be used, was highly testable.
return new Promise((resolve, reject) => {
reject("err");
}).catch(err => {
this.emit("uncaughtException", err);
/* Throw so the promise is still rejected for testing */
throw err;
});
According the spec (see 3.III.d):
d. If calling then throws an exception e,
a. If resolvePromise or rejectPromise have been called, ignore it.
b. Otherwise, reject promise with e as the reason.
That means that if you throw exception in then function, it will be caught and your promise will be rejected. catch don't make a sense here, it is just shortcut to .then(null, function() {})
I guess you want to log unhandled rejections in your code. Most promises libraries fires a unhandledRejection for it. Here is relevant gist with discussion about it.
Yes promises swallow errors, and you can only catch them with .catch, as explained more in detail in other answers. If you are in Node.js and want to reproduce the normal throw behaviour, printing stack trace to console and exit process, you can do
...
throw new Error('My error message');
})
.catch(function (err) {
console.error(err.stack);
process.exit(0);
});
I know this is a bit late, but I came across this thread, and none of the solutions were easy to implement for me, so I came up with my own:
I added a little helper function which returns a promise, like so:
function throw_promise_error (error) {
return new Promise(function (resolve, reject){
reject(error)
})
}
Then, if I have a specific place in any of my promise chain where I want to throw an error (and reject the promise), I simply return from the above function with my constructed error, like so:
}).then(function (input) {
if (input === null) {
let err = {code: 400, reason: 'input provided is null'}
return throw_promise_error(err)
} else {
return noterrorpromise...
}
}).then(...).catch(function (error) {
res.status(error.code).send(error.reason);
})
This way I am in control of throwing extra errors from inside the promise-chain. If you want to also handle 'normal' promise errors, you would expand your catch to treat the 'self-thrown' errors separately.
Hope this helps, it is my first stackoverflow answer!
Listen for unhandled errors:
window.addEventListener('unhandledrejection', e => {
// ...
});
window.addEventListener('error', e => {
// ...
});
If error gets swallowed, use self.report(error):
.catch(error => {
self.reportError(error);
});
https://developer.mozilla.org/en-US/docs/Web/API/reportError

Categories