does promise catch too much errors? - javascript

I am writing promise following this style in the doc:
Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();
The catch clause will catch any errors including typos.
However, according to nodejs dos:
By the very nature of how throw works in JavaScript, there is almost never any way to safely "pick up where you left off", without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process.
Some kinds of errors would throw out if we are writing code in the callback style, but not in promise style
This is really confusing me. How should I avoid leaking references when writing in promise.
Thanks~

The example shows good promise chain, including using .done() to make sure any unhandled exceptions are thrown from the promise chain to the outside application.
As far as references and error handling: the promise chains only guarantee than an error will be forwarded to the .catch callback. If there is no way to clean up the state when the error is thrown - you are out of luck. For example
Q.fncall(function firstStep() {
var fs = open file reference
foo.bar; // generates ReferenceError
}).then(function somethingElse() {
...
}).catch(function (err) {
// we have caught ReferenceError
// but we cannot clean up open fs reference!
}).done();
We caught the error, but the catch handler cannot close fs reference. This is what it means that even with promises we have to think how to clean up resources in case of an error.

Related

With Javascript Promises, are there best practices regarding the use of "error" versus catch clauses?

In learning to write in JavaScript with Promises, I'm encountering two different ways of dealing with errors. One is to use a catch, as in this example:
axios.post('/someapi', {
somedata1: 'foo'
})
.then((result) => {
console.log(result);
})
.catch((exception) => {
console.log(exception);
});
The other is to have a clause in the then for the rejected case:
axios.post('/someapi', {
somedata1: 'foo',
})
.then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
Some authors seem to use one approach, other authors use the other, and it's not clear to me why. Are there situations in which it's necessary or desirable to use one or the other?
(The example above uses axios, but that's just for the purposes of providing a code example. This question is not about axios.)
With Javascript Promises, are there best practices regarding the use of “error” versus catch clauses?
There is no universal "best practice" for that question because it depends upon what specific behavior you want your code to have. As others have mentioned, you get a different behavior in a few ways.
Some authors seem to use one approach, other authors use the other, and it's not clear to me why. Are there situations in which it's necessary or desirable to use one or the other?
Yes, there are situations to use one or the other. They provide potentially different behaviors. Only if you're 200% sure that your successHandler in .then(successHandler) can never throw or return a rejected promise, would there be no meaningful difference.
As a summary:
When using p.then(successHandler).catch(errorHandler), errorHandler will get errors that occur either from a rejected p or from an error or rejection from the successHandler.
When using p.then(successHandler, errorHandler), errorHandler will be called from a rejection of p and will NOT get called from an error or rejection from the successHandler.
Different behaviors that are useful for different circumstances.
In this .catch() example below, the .catch() will catch an error that occurs (either accidentally from a coding error, a thrown exception or when returning some other promise that rejects).
Promise.resolve("hello").then(greeting => {
console.log("throwing error");
throw new Error("My .then() handler had an error");
}).catch(err => {
// will get here
console.log("Caught error in .catch()\nError was: ", err.message);
});
But, when using the second argument to .then(), that error from the .then() handler will not be caught:
Promise.resolve("hello").then(greeting => {
console.log("throwing error");
throw new Error("My .then() handler had an error");
}, err => {
// won't get here
console.log("Caught error in .catch()\nError was: ", err.message);
});
So, sometimes you want an error that might occur in the .then() handler to hit this immediate error handler and sometimes you don't want it to hit that error handler because you want that error handler to only process errors from the original promise and you have some other catch handler later in the promise chain that will deal with this error.
Recommendation
In general, I would advise that you start out with the .catch() handler because it catches more errors and does not require that there be some other .catch() handler elsewhere in the promise chain in order to be safe. Then, you switch to the .then(successHandler, errorHandler) form if you explicitly don't want this errorHandlerto be called if there's another error in the successHandler AND you have somewhere else in the promise chain where an error in the successHandler would get caught or handled.
Both the syntaxes work out of the box. But the first one has an advantage that the Catch block is able to catch an error thrown by the Promise Rejection as well as an error thrown by then block.
Here is the Example you can try in Node and you will have a better idea about it.
function x () {
return new Promise((resolve, reject) => {
return resolve('Done...');
});
}
x()
.then(response => {
console.log('RESPONSE---', response)
throw 'Oops...Error occurred...';
})
.catch(error => console.log('ERROR---', error));
x()
.then(
response => {
console.log('RESPONSE---', response)
throw 'Oops...Error occurred...';
},
error => console.log('ERROR---', error)
);

Prevent of “Uncaught (in promise)” warning. How to avoid of 'catch' block? ES6 promises vs Q promises

My question consist of two parts:
Part 1
According to standard ES6 Promise I see that I forced to use catch block everywhere, but it looks like copy/paste and looks weird.
Example:
I have some class which makes request to backend (lets call it API class).
And I have a few requirements for API class using:
1) I need to make requests in different parts of my application with single request errors processing:
// somewhere...
api.getUser().then(... some logic ...);
// somewhere in another module and in another part of app...
api.getUser().then(... some another logic...);
2) I want so 'then' blocks would work ONLY when 'getUsers' succeeded.
3) I don't want to write catch block everywhere I use api.getUsers()
api.getUser()
// I don't want following
.catch(e => {
showAlert('request failed');
})
So I'm trying to implement single error processing inside of the class for all "users requests"
class API {
getUser() {
let promise = makeRequestToGetUser();
promise.catch(e => {
showAlert('request failed');
});
return promise;
}
}
...but if request fails I still forced to use catch block
api.getUser()
.then(... some logic...)
.catch(() => {}) // <- I forced to write it to avoid of “Uncaught (in promise)” warning
... otherwise I'll get “Uncaught (in promise)” warning in console. So I don't know the way of how to avoid of .catch block everywhere I use api instance.
Seems this comes from throwing error in such code:
// This cause "Uncaught error"
Promise.reject('some value').then(() => {});
May be you can say 'just return in your class "catched" promise'.
class API {
getUser() {
return makeRequestToGetUser().catch(e => {
showAlert('request failed');
return ...
});
}
}
...but this contradict to my #2 requirement.
See this demo: https://stackblitz.com/edit/promises-catch-block-question
So my 1st question is how to implement described logic without writing catch block everywhere I use api call?
Part 2
I checked if the same API class implementation with Q library will get the same result and was surprised because I don't get “Uncaught (in promise)” warning. BTW it is more expectable behavior than behavior of native ES6 Promises.
In this page https://promisesaplus.com/implementations I found that Q library is implementation of Promises/A+ spec. But why does it have different behavior?
Does es6 promise respects Promises/A+ spec?
Can anybody explain why these libraries has different behavior, which one is correct, and how implement mentioned logic in case if "ES6 Promises implementation" is correct?
I see that I forced to use catch block everywhere
No, you don't need to do that. Instead, return the promise created by then to the caller (and to that caller's caller, and...). Handle errors at the uppermost level available (for instance, the event handler that started the call sequence).
If that's still too many catchs for you, you can hook the unhandledrejection event and prevent its default:
window.addEventListener('unhandledrejection', event => {
event.preventDefault();
// You can use `event.reason` here for the rejection reason, and
// `event.promise` for the promise that was rejected
console.log(`Suppressed the rejection '${event.reason.message}'`);
});
Promise.reject(new Error("Something went wrong"));
The browser will trigger that event prior to reporting the unhandled rejection in the console.
Node.js supports this as well, on the process object:
process.on('unhandledRejection', error => {
// `error` is the rejection reason
});
Note that you get the reason directly rather than as a property of an event object.
So I don't know the way of how to avoid of .catch block everywhere I use api instance.
Surely the caller of getUser needs to know it failed? I mean, if the answer to that is really "no, they don't" then the event is the way to go, but really the code using api should look like this:
function useTheAPI() {
return getUser()
.then(user => {
// Do something with user
});
}
(or the async equivalent) so that the code calling useTheAPI knows that an error occurred; again, only the top-level needs to actually handle the error.
Can anybody explain why these libraries has different behavior, which one is correct, and how implement mentioned logic in case if "ES6 Promises implementation" is correct?
Both are correct. Reporting unhandled exceptions entirely in userland (where libraries live) is hard-to-impossible to do such that there aren't false positives. JavaScript engines can do it as part of their garbage collection (e.g.: if nothing has a reference to the promise anymore, and it was rejected, and nothing handled that rejection, issue the warning).

Handling Errors early in the Promise Chain

I'm trying to find a general way to handle errors on promise chains. In the following snipped I'd like to handle an potential connection-error directly in my connection.js.
connection.js
function getData() {
return fetch(myEndpoint)
.catch(err => handleConnectionError(err)) // redirect to error page or similar
}
app.js
// import connection
connection.getData()
.then(data => handleData(data.foo))
So there are two ways this scenario could play out:
If I catch the error in connection.js without throwing a new one, it will continue the promise chain and handleData() will fail
If I throw an Error again after handling it, the Promise chain wont be executed any further, but then I have an unhandled-promise-rejection error in the console.
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
The best way to go about this would be to have a final catch to take care of all errors. E.G:
function errorHandler(e) {
if (e instanceof x) {
//handle here
}
}
And then:
fetch(endPoint).then(doSomething).then(doAnotherThing).catch(err => errorHandler(err))
If fetch or any other promise along the chain produces an error, the rest of the then() statements will be skipped and will be catched by the final catch function. From there you will need to make sure that all types of errors that could be thrown by all promises are taken care of.
However, you will need to get rid of the catch in getData as it will cause the rest of the chain to run as usual since the error is taken care of.
Hope that helps
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
Yes, that's precisely what you should do:
function getData() {
return fetch(myEndpoint)
}
// elsewhere:
connection.getData().then(handleData, handleConnectionError);
You usually don't know whether you always want to "redirect to error page or similar". Maybe at some places in your app, you can handle connection errors by faking response data, and in other places you might want to show an error message instead of triggering a redirect.

Catching errors with Q.deferred

I have a nodeJS project where I wish to use asynchronous functions. Namely, I'm using the Q library.
I have a function called someFunction(), that I wish to return a promise. With the then -function, I can then check whether the promise was resolved or rejected like so:
someFunction()
.then(
function(results) {
console.log("Success!");
},
function (error) {
console.log("An error occurred, and I would wish to log it for example");
}
);
What I intuitively expect with the function above is, that the error function would catch all possible errors. So if some exception is raised inside the someFunction, I can rest assured, that the error function will be run (the second function after 'then'). But it seems this is not the case.
For example, let's say the someFunction would be defined as so:
function someFunction() {
var deferred = Q.defer();
throw new Error("Can't bar.");
deferred.resolve("success");
}
Now if I call the function someFunction() like done in the upper code block, it won't run the error function. Why is that? Isn't the partial point of the promise Q.deferred to catch errors? Why should I manually reject every error that happens? I know I could set the whole content of someFunction to try/catch clause, and then reject the deferred, but that feels so wrong! There must be a better way, and I know for sure some of you stackoverflowers know it!
With this information, I began to think about where the deferred.reject and deferred.resolve is even meant to be used? Is it even meant to catch exceptions? Should I really just go through all the error cases manually, and call the deferred.reject on them? I'm interested to hear how this should be handled professionally.
Q has specific function for success and error, so use:
deferred.reject("error");
intead of throwing an Exception.
Next thing is that someFunction must return promise to be used as You use it:
function someFunction() {
var deferred = Q.defer();
try{
//some Asynchronous code
deferred.resolve("success");
}catch(e){
deferred.reject(e.message);
}
return deffered.promise; //return promise to use then
}
Because Promises ain't magic. They don't somehow magically catch Errors. They catch them, because they wrap the calls to the callbacks in try..catch-blocks, to convert Errors into rejected Promises.
If you want an Error to be handled by the Promise-chain, well put the function-call into a promise-chain: Q.resolve().then(someFunction).then(...).
Now any synchronous Error occuring in someFunction can be handled in the following then's.
Btw: If you use Q.defer(), and you're not dealing with some callback-style API, you're doing it definitely wrong. Search for the Deferred-antipattern.
it won't run the error function. Why is that?
Because you synchronously threw an exception, instead of returning a promise. Which you never should.
Isn't the partial point of the promise Q.deferred to catch errors?
No. then and catch implicitly catch exceptions in their callbacks, defferreds don't - they're just a (deprecated) API to create promises.
Why should I manually reject every error that happens?
Because asynchronous errors are expected to be passed to callbacks anyway, instead of being thrown.
I know I could set the whole content of someFunction to try/catch clause, and then reject the deferred, but that feels so wrong! There must be a better way!
There is: the Q.Promise constructor, the standard (ES6) promise creation API. It has the benefit of being able to catch synchronous exceptions from the executor function:
function someFunction() {
return new Q.Promise(function(resolve) {
throw new Error("Can't bar.");
resolve("success");
});
}
throwing an error will stop the code from executing (and will close node) unless you catch the error in a try/catch block.
handled errors from requests can be passed to the .catch chain by using deferred.reject(error). code errors and custom thrown errors needs to be handled inside try/catch, which is the right way to handle such errors.
function someFunction() {
var deferred = Q.defer();
deferred.reject("Can't bar.");
// or
try {
throw new Error("Can't bar.");
}
catch(err) {
deferred.reject("Can't bar.");
}
deferred.resolve("success");
}

How to return from a Promise's catch/then block?

There are many tutorials on how to use "then" and "catch" while programming with JavaScript Promise. However, all these tutorials seem to miss an important point: returning from a then/catch block to break the Promise chain. Let's start with some synchronous code to illustrate this problem:
try {
someFunction();
} catch (err) {
if (!(err instanceof MyCustomError))
return -1;
}
someOtherFunction();
In essence, I am testing a caught error and if it's not the error I expect I will return to the caller otherwise the program continues. However, this logic will not work with Promise:
Promise.resolve(someFunction).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction);
This logic is used for some of my unit tests where I want a function to fail in a certain way. Even if I change the catch to a then block I am still not able to break a series of chained Promises because whatever is returned from the then/catch block will become a Promise that propagates along the chain.
I wonder if Promise is able to achieve this logic; if not, why? It's very strange to me that a Promise chain can never be broken. Thanks!
Edit on 08/16/2015:
According to the answers given so far, a rejected Promise returned by the then block will propagate through the Promise chain and skip all subsequent then blocks until is is caught (handled). This behavior is well understood because it simply mimics the following synchronous code (approach 1):
try {
Function1();
Function2();
Function3();
Function4();
} catch (err) {
// Assuming this err is thrown in Function1; Function2, Function3 and Function4 will not be executed
console.log(err);
}
However, what I was asking is the following scenario in synchronous code (approach 2):
try {
Function1();
} catch(err) {
console.log(err); // Function1's error
return -1; // return immediately
}
try {
Function2();
} catch(err) {
console.log(err);
}
try {
Function3();
} catch(err) {
console.log(err);
}
try {
Function4();
} catch(err) {
console.log(err);
}
I would like to deal with errors raised in different functions differently. It's possible that I catch all the errors in one catch block as illustrated in approach 1. But that way I have to make a big switch statement inside the catch block to differentiate different errors; moreover, if the errors thrown by different functions do not have a common switchable attribute I won't be able to use the switch statement at all; under such a situation, I have to use a separate try/catch block for each function call. Approach 2 sometimes is the only option. Does Promise not support this approach with its then/catch statement?
This can't be achieved with features of the language. However, pattern-based solutions are available.
Here are two solutions.
Rethrow previous error
This pattern is basically sound ...
Promise.resolve()
.then(Function1).catch(errorHandler1)
.then(Function2).catch(errorHandler2)
.then(Function3).catch(errorHandler3)
.then(Function4).catch(errorHandler4)
.catch(finalErrorHandler);
Promise.resolve() is not strictly necessary but allows all the .then().catch() lines to be of the same pattern, and the whole expression is easier on the eye.
... but :
if an errorHandler returns a result, then the chain will progress to the next line's success handler.
if an errorHandler throws, then the chain will progress to the next line's error handler.
The desired jump out of the chain won't happen unless the error handlers are written such that they can distinguish between a previously thrown error and a freshly thrown error. For example :
function errorHandler1(error) {
if (error instanceof MyCustomError) { // <<<<<<< test for previously thrown error
throw error;
} else {
// do errorHandler1 stuff then
// return a result or
// throw new MyCustomError() or
// throw new Error(), new RangeError() etc. or some other type of custom error.
}
}
Now :
if an errorHandler returns a result, then the chain will progress to the next FunctionN.
if an errorHandler throws a MyCustomError, then it will be repeatedly rethrown down the chain and caught by the first error handler that does not conform to the if(error instanceof MyCustomError) protocol (eg a final .catch()).
if an errorHandler throws any other type of error, then the chain will progress to the next catch.
This pattern would be useful if you need the flexibility to skip to end of chain or not, depending on the type of error thrown. Rare circumstances I expect.
DEMO
Insulated Catches
Another solution is to introduce a mechanism to keep each .catch(errorHandlerN) "insulated" such that it will catch only errors arising from its corresponding FunctionN, not from any preceding errors.
This can be achieved by having in the main chain only success handlers, each comprising an anonymous function containing a subchain.
Promise.resolve()
.then(function() { return Function1().catch(errorHandler1); })
.then(function() { return Function2().catch(errorHandler2); })
.then(function() { return Function3().catch(errorHandler3); })
.then(function() { return Function4().catch(errorHandler4); })
.catch(finalErrorHandler);
Here Promise.resolve() plays an important role. Without it, Function1().catch(errorHandler1) would be in the main chain the catch() would not be insulated from the main chain.
Now,
if an errorHandler returns a result, then the chain will progress to the next line.
if an errorHandler throws anything it likes, then the chain will progress directly to the finalErrorHandler.
Use this pattern if you want always to skip to the end of chain regardless of the type of error thrown. A custom error constructor is not required and the error handlers do not need to be written in a special way.
DEMO
Usage cases
Which pattern to choose will determined by the considerations already given but also possibly by the nature of your project team.
One-person team - you write everything and understand the issues - if you are free to choose, then run with your personal preference.
Multi-person team - one person writes the master chain and various others write the functions and their error handlers - if you can, opt for Insulated Catches - with everything under control of the master chain, you don't need to enforce the discipline of writing the error handlers in that certain way.
First off, I see a common mistake in this section of code that could be completely confusing you. This is your sample code block:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction()); // <== Issue here
You need pass function references to a .then() handler, not actually call the function and pass their return result. So, this above code should probably be this:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
// returning a normal value here will take care of the rejection
// and continue subsequent processing
return -1;
}
}).then(someOtherFunction); // just pass function reference here
Note that I've removed () after the functions in the .then() handler so you are just passing the function reference, not immediately calling the function. This will allow the promise infrastructure to decide whether to call the promise in the future or not. If you were making this mistake, it will totally throw you off for how the promises are working because things will get called regardless.
Three simple rules about catching rejections.
If nobody catches the rejection, it stops the promise chain immediately and the original rejection becomes the final state of the promise. No subsequent handlers are invoked.
If the promise rejection is caught and either nothing is returned or any normal value is returned from the reject handler, then the reject is considered handled and the promise chain continues and subsequent handlers are invoked. Whatever you return from the reject handler becomes the current value of the promise and it as if the reject never happened (except this level of resolve handler was not called - the reject handler was called instead).
If the promise reject is caught and you either throw an error from the reject handler or you return a rejected promise, then all resolve handlers are skipped until the next reject handler in the chain. If there are no reject handlers, then the promise chain is stopped and the newly minted error becomes the final state of the promise.
You can see a couple examples in this jsFiddle where it shows three situations:
Returning a regular value from a reject handler, causes the next .then() resolve handler to be called (e.g. normal processing continues),
Throwing in a reject handler causes normal resolve processing to stop and all resolve handlers are skipped until you get to a reject handler or the end of the chain. This is effective way to stop the chain if an unexpected error is found in a resolve handler (which I think is your question).
Not having a reject handler present causes normal resolve processing to stop and all resolve handlers are skipped until you get to a reject handler or the end of the chain.
There is no built-in functionality to skip the entirety of the remaining chain as you're requesting. However, you could imitate this behavior by throwing a certain error through each catch:
doSomething()
.then(func1).catch(handleError)
.then(func2).catch(handleError)
.then(func3).catch(handleError);
function handleError(reason) {
if (reason instanceof criticalError) {
throw reason;
}
console.info(reason);
}
If any of the catch blocks caught a criticalError they would skip straight to the end and throw the error. Any other error would be console logged and before continuing to the next .then block.
If you can use the newer async await this is pretty simple to implement:
async function myfunc() {
try {
return await anotherAsyncFunction();
} catch {
//do error handling
// can be async or not.
return errorObjct();
}
}
let alwaysGetAValue = await myfunc();
Depending on what technology your using you may need some kind of high level wrapper function to allow for the top level await.

Categories

Resources