In most Angular libraries (and specifically ng-boostrap) there is a way to open modals on the page that block the user from doing anything until they every "close" the modal or "dismiss" the modal. In ng-bootstrap it looks like this.
try {
let handle = this.nbgModal.open(...);
await handle.result;
// any code here won't run until after the modal is 'closed'
}catch(e){
// however, if the user 'dismisses' the modal then the catch block is ran
// so what happens in here if a real exception is thrown???
}
The response of the open(...) method allows you to use async/await keywords to wait for the modal to be interacted with by the user before proceeding to the rest of the code.
If the user "closes" the model, the Promise is resolved and we continue using some result from the modal possibly. However, if the user "dismisses" the modal then we will reject the Promise and the code in the catch block will run.
So now the final question... what happens when a true exception is thrown in any of the code in the try block above? In this example we would swallow the exception and the user/developer would never know since the console won't emit an error.
Use promises for modal like this has been so methign I've seen for years, so what is the expected solution for determining whether you are dealing with a valid business scenario (e.g. dismissal) vs. an exception that is throw due to a bug?
I have a solution to this but I feel like I would be fighting against these libraries to build another service that does ever reject unless theres a true exception. Am I missing somethign that should allow me to do this?
I wouldn't blame the developers of libs. Raising expectation on the promise rejection when using await seems doubtful as well.
try {
let handle = this.nbgModal.open(...);
handle.result
.then(result=>doSmgWithDialogResult(result))
.catch(()=>doSmgWhenDialogDIsmnissed());
} catch(e) {
// catch exceptions
}
Related
I hope there is a general consensus to this question and I'm not asking for people's opinions.
My component dispatches a action method:
await dispatch('someAction', payload)
Store
async someAction({commit}, payload){
try {
const data = await http.get('/foobar', payload)
commit('someCommit', data.data)
} catch (error) {
throw error
}
}
In the store method, the try/catch is throwing an Eslint error unnecessary try/catch, which to me doesn't make sense. The server can throw an error and the http call can fail, so in order to avoid the commit from firing I added a try/catch block. I guess I could add a if (data) commit(... but isn't it cleaner with a try/catch?
Also, in the catch I'm throwing the error so that the original dispatch call can be stopped (if it was in its own try/catch).
Am I doing things wrong here? Is there a 'better' way of doing things?
From the docs
A catch clause that only rethrows the original error is redundant, and has no effect on the runtime behavior of the program. These redundant clauses can be a source of confusion and code bloat, so it’s better to disallow these unnecessary catch clauses.
The preferred way to handle an error is to... well, handle it. Log it, respond to it in some way, just don't throw it. However, as the docs also say,
If you don’t want to be notified about unnecessary catch clauses, you can safely disable this rule.
Which can be done in your eslint config:
"rules": {
"no-useless-catch": "off" // "off" or 0 both work
}
** Try catch will instantly hault that stack and fire the catch... You can put any type of callback in there... Like for instance you can recursively function again to retry ...
Also you dont have to worry about dispatches and stuff like that in pinia just use this inside the action as if it were a normal javascript class.
state =( (){
color = "red"
},
actions{
changeColors{
try{
const colors = await api.getColors()
this.colors = colors.data // this doesnt fire if the request fails
}
catch(error){
// were only here because of the fact that the runtime
// threw an error... If we dont catch it nodejs crashes...
// now you do anything you want here -> -> -> but just know the code
// above did not complete... so thats what you do here.
console.log(error)
anyFunction(error)
}
},
})
When you catch an error ... You are CATCHING an error that was thrown inside the try block... So to go around in circles and then rethrow it... Is a waste of memory its like console logging a console log...
This is why AI generated code is useless(just guessing you generated this off of something... Not saying it in a negative way.. u obv know what code is , but you wouldnt of used throw there, you would of used something like console.log(error.message or whatever else you care about. Or used some type of middleware to forward it back to the user.
). When you dont throw an error it causes the runtime to terminate. When you catch the error... You are trying to prevent that from happening by turning it into an event listener esentially... When an error is thrown do this... To turn around and throw it ... is a waste of resourses especially if u are using that type of design pattern on something like react where or vue where your doing the same thing hundreds of times in little variated ways.
Is there a substantial difference between throwing vs returning an error from a service class?
class ProductService {
async getProduct(id) {
const product = await db.query(`...`)
if (!product) {
throw new ProductNotFoundError()
}
return product
}
// VS
async getProduct(id) {
const product = await db.query(`...`)
if (!product) {
return {
data: null,
error: new ProductNotFoundError()
}
}
return {
error: null,
data: product
}
}
}
In this example, I'd like to raise an error message if a product is not found ( supposedly returning null is not enough, maybe I need to also provide additional info why the product is not found ).
As a general design principle, is there a preferred approach, are there any substantial pros/cons of both approaches?
I've been doing the throw new Error example, but then the code gets riddled with try/catch or .then/.catch statements, which make it somewhat harder to reason about.
What I don't particularly like about throwing errors is.. they are not unexpected. Obviously, I expect that a product might not be found, so I added a piece of logic to handle that part. Yet, it ends up being received by the caller the same way, for example, a TypeError is received. One is an expected domain error, the other one is unexpected exception.
In the first scenario, if I return an object with data and error properties ( or I can return an object with methods like isError, isSuccess and getData from each service, which is similar ), I can at least trust my code to not raise exceptions. If one happens to arise, then it will be by definition unexpected and caught by the global error handlers ( express middleware, for example ).
Is there a substantial difference between throwing vs returning an error from a service class?
Big difference. throw in an async function causes the promise that is returned from any async function to be rejected with that exception as the reject reason. Returning a value becomes the resolved value of the promise.
So, the big difference is whether the promise returns from the async function is resolved with a value or rejected with a reason.
As a general design principle, is there a preferred approach, are there any substantial pros/cons of both approaches?
Most people use promise rejections for unusual conditions where the caller would typically want to stop their normal code flow and pursue a different code path and use return values for something that is likely to continue the normal code flow. Think about chained promises:
x().then(...).then(...).catch(...)
If the condition should go immediately to the .catch() because something serious is busted, then a throw/rejection makes the most sense in x(). But, if the condition is just something to typically handle and continue the normal execution, then you would want to just return a value and not throw/reject.
So, you try to design x() to be the easiest for the expected use. A product not being found in the database is an expected possible condition, but it also might be something that the caller would want to abort the logic flow they were in, but the treatment of that condition will certainly be different than an actual error in the database.
As a reference example, you will notice that most databases don't treat a failure to find something in a query as an error. It's just a result of "not found".
This question already has answers here:
When is .then(success, fail) considered an antipattern for promises?
(7 answers)
Closed 4 years ago.
I have come across multiple applications where using catch is preferred over rejectHandler.
Eg:
Preferring
new Promise.then(resolveHandler).catch()
instead of
new Promise().then(resolveHandler, rejectHandler).catch()
Is there a particular reason for this??
I find
new Promise().then(resolveHandler, rejectHandler).catch()
to be more useful because
I can use rejectHandler to address designed/expected error scenario where Promise.reject is called.
I can use catch block to address unknown/unexpected programming/runtime errors that occur.
Does someone know any particular reason why rejectHandler is not used much?
P.S. I am aware of newer alternatives in ES6 but I just curious to know this.
Update: I KNOW HOW rejectHandler and catch works. The question is why do I see more people use only catch over both rejectHandler and catch? Is this a best practice or there is some advantage?
Update(Adding answer here): Found the answer I was looking for first hand.
The reason is not just because the error in reject is handled by catch it is mainly because of chaining. When we are chaining promise.then.then.then.then, having a resolve, reject pattern proves a bit tricky to chain it since you wouldn't want to implement a rejecthandler just to forward the rejectData up the chain. Using only promise/then/catch along with resolve/return/throw proves very useful in chaining N numbers of thenables.
#Bob-Fanger(accepted answer) addressed some part of this too.
Eg:
getData(id) {
return service.getData().then(dataList => {
const data = dataList.find(data => {
return data.id === id;
});
if (!data) {
// If I use Promise.reject here and use a reject handler in the parent then the parent might just be using the handler to route the error upwards in the chain
//If I use Promise.reject here and parent doesn't use reject handler then it goes to catch which can be just achieved using throw.
throw {
code: 404,
message: 'Data not present for this ID'
};
}
return configuration;
});
}
//somewhere up the chain
....getConfiguration()
.then(() => {
//successful promise execution
})
.catch(err => {
if (err.code) {
// checked exception
send(err);
} else {
//unchecked exception
send({
code: 500,
message: `Internal Server error: ${err}`
});
}
});
Using just these All I need to worry about is promise/then/catch along with resolve/return/throw anywhere in the chain.
The difference is that if an error occurs inside resolveHandler it won't be handled by the rejectHandler, that one only handles rejections in the original promise.
The rejectHandler is not used in combination with catch that much, because most of the time we only care about that something went wrong.
Creating only one errorhandler makes the code easier to reason about.
If a specific promise in the chain should handled differently that can be a reason to use a rejectHandler, but i'd probably write a catch().then().catch() in that case.
Neither is more useful than the other. Both the rejected handler and the catch callback are called when an error is thrown or a promise is rejected.
There is no "best practice" to use one over the other. You may see code use one or the other, but it's use will be based on what the code needs to achieve. The programmer may want to catch an error at different times in the chain and handle errors thrown at different times differently.
Hopefully the following will help explain what I mean:
somePromise
.then(
function() { /* code when somePromise has resolved */ },
function() {
/* code when somePromise has thrown or has been rejected.
An error thrown in the resolvedHandler
will NOT be handled by this callback */ }
);
somePromise
.then(
function() { /* code when somePromise has resolved */ }
)
.catch(
function() {
/* code when somePromise has thrown or has been rejected OR
when whatever has occurred in the .then
chained to somePromise has thrown or
the promise returned from it has been rejected */ }
);
Notice that in the first snippet, if the resolved handler throws then there is no rejected handler (or catch callback) that can catch the error. An error thrown in a resolved callback will not be caught by the rejectedHandler that is specified as the second argument to the .then
As stated in the post, provision of a resolve and reject handler in the same call to .then allows dealing with rejection of the previous promise separately from errors thrown within, or returning a rejected promise from, the success handler.
Because a rejection handler returning without throwing an error resumes the fufilled channel of a promise chain, a final catch handler will not be invoked if a previous rejection handler returns normally.
The question then devolves into use cases, cost of development and level of knowledge.
Use cases
In theory the two parameter form of then call could be used to retry an operation. But because hard coded promise chains are set up statically, retrying the operation is not simple. An easier way to retry might be to use an async function with try-catch statements surrounding await of a promise that may need to be retried as in this concept code:
async function() {
let retries = 3;
let failureErr = null;
while( retries--) {
try {
var result = await retryableOperationPromise()
return result;
}
catch( err) {
failureErr = err;
}
}
throw failureErr // no more retries
}
Other use cases may not be widespread.
Cost of development or commercial decisions.
If telling a user to retry later is acceptable it may be cheaper than doing anything about specific reasons for promise rejection. For example if I try to book a flight over midnight, when airlines put the prices up, I usually get told "an error occurred, try again later" because the price I was given at the start of booking will not be honored.
Knowledge (<opinion>)
I suspect that promise usage may often be based on example rather than in-depth knowledge of the subject. It is also possible program managers want to keep the code base as simple as possible for less experienced developers (probably a cost issue).
"Best practice" may not truly apply for making decisions on how to use promises if the usage is valid. Some developers and managers will avoid some forms of usage as a matter of principle, but not always based on technical merit.
Recently I had a discussion with my coworker, about using try and catch to notify errors or avoid them.
This is my coworker's approach:
import Config from 'config';
export const getUserFromLocalStorage = () => {
const key = Object.keys(localStorage).find(value => value === `${Config.applicationId}/currentUser`);
try {
return key ? JSON.parse(localStorage[key]) : {};
} catch (e) {
return {};
}
};
Wich means, he doesn't care about the given error and he is just carrying of returning an object in order to continue the process
and mine is:
import Config from 'config';
export const getUserFromLocalStorage = () => {
const key = Object.keys(localStorage).find(value => value === `${Config.applicationId}/currentUser`);
try {
return key ? JSON.parse(localStorage[key]) : {};
} catch (e) {
console.log('the given error', e); // Just simple notifier for this example
}
};
but my approach, still has a problem which is that it will return undefined (which can crash internaly my app), that can easily fix it using finally and returning a default value but it doesn't sound a good practice to me.
THE QUESTION
So what will the balance using try catch and finally if needed, to make my application stable.
Is there something wrong with our approach?
In particular, we can't trust the data that is comming from the localStorage, so what will be the best approach for this implementation?
Since finally is executed in either case, whether something was thrown or not, it is not the place to return a default value. It is also questionable whether you need to log the error in great detail or not. It all depends on whether something is an expected error or a truly exceptional circumstance and who can do something about it.
Is it rather likely or possible that the value stored is invalid JSON? And you have a "backup plan" for what to do in that case? And there's nothing the user and/or developer can do about it? Then don't bother anyone with it. Maybe you want to console.log a message that might aid in debugging, but other than that just carry on with the program flow. There's most certainly no need to bug the user with an alert if a) the user didn't initiate the action and b) there's nothing for them to do about it either.
Considerations to take:
Whether to catch an error in the first place:
is it an expected error which may naturally occur during the program flow?
is it an error you can do something about?
do you have any plan what to do if you caught the error?
Whether to log an error:
does this log do anyone any good?
will anybody ever see that log entry?
does it give anyone any useful information that can lead to fixing the issue?
Whether to bug the user about something:
did the user initiate the action?
does the user expect some form of response, positive or negative?
can the user do anything to fix the problem?
Whether to return an empty object or nothing/null/undefined depends on what the function's responsibility is. Is the function defined to always return an object? Then it should return {} from catch. Or is "nothing" a valid response when the expected object doesn't exist? Then perhaps return false.
Overall, your coworker's approach seem very reasonable to me.
In this specific case, where you are working with localStorage (which almost always inevitably means working with JSON.parse()), it's always best practice to wrap your processing in try-catch. This is because both localStorage and JSON.parse have exceptions as a normal part of their error handling and it is usually possible to fallback to a default or initial value gracefully.
A pattern I use is something like as follows:
const DEFAULT_VALUE = {};
try {
const result = JSON.parse(result);
return result || DEFAULT_VALUE;
} catch (e) {
console.warn('Error parsing result', e);
}
return DEFAULT_VALUE;
This way, you have consistent error handling and default value fallback.
In general, you shouldn't need use try-catch unless you can and will safely handle the error and generate a useful fallback. Most try-catch blocks tend to sit at the bottom of a call-stack for this reason, so that they catch unplanned errors, handle them gracefully for the user, but log them noisily with a call-stack to the console for the developer to investigate/handle correctly/workaround.
I think the most important thing is that user satisfaction. At the end of the day, the program is used by the normal user. The user needs to continue his work using the program without any interruptions.
So i think that the best practices is to use try to run code and catch if there any errors and notify developer and/or user that there is an exception, and use finally to overcome the exception by returning a valid object.
In this way user is also able to continue the work and the developer also can check the error in log file for future debugging.
This is my personal idea.
Short story:
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
Longer version, described as real life problem (would love someone to read):
I have a library that uses Bluebird (A+ Promise implementation library), to fetch data from database (talking about Sequelize). Every query returns a result, but sometimes it's empty (tried to select something, but there wasn't any). The promise goes into the result function, because there is no reason for an error (not having results is not an error). Example:
Entity.find(1).then(function(result) {
// always ending here, despite result is {}
}).catch(function(error) {
// never ends here
});
I want to wrap this and check if the result is empty (can't check this everywhere in my code). I did this:
function findFirst() {
return Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
return result; // will invoke the success function
} else {
// I WANT TO INVOKE THE ERROR, HOW?! :)
}
}).catch(function(error) {
// never ends here
});
}
findFirst().then(function(result) {
// I HAVE a result
}).catch(function(error) {
// ERROR function - there's either sql error OR there is no result!
});
If you are still with me - I hope you will understand what's up. I want somehow to fire up the error function (where "ERROR function" is). The thing is - I don't know how. I've tried these things:
this.reject('reason'); // doesn't work, this is not a promise, Sequelize related
return new Error('reason'); // calls success function, with error argument
return false; // calls success function with false argument
throw new Error('reason'); // works, but if .catch is missing => BLOW!
As you can see by my comments (and per spec), throwing an error works well. But, there's a big but - if I miss the .catch statement, my whole app blows up.
Why I don't want this? Let's say I want to increment a counter in my database. I don't care about the result - I just make HTTP request.. So I can call incrementInDB(), which has the ability to return results (even for test reasons), so there is throw new Error if it failed. But since I don't care for response, sometimes I won't add .catch statement, right? But now - if I don't (on purpose or by fault) - I end up with your node app down.
I don't find this very nice. Is there any better way to work it out, or I just have to deal with it?
A friend of mine helped me out and I used a new promise to fix things up, like this:
function findFirst() {
var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer
Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
deferred.resolve(result);
} else {
deferred.reject('no result');
}
}).catch(function(error) {
deferred.reject('mysql error');
);
return deferred.promise; // return a promise, no matter of framework
}
Works like a charm! But I got into this: Promise Anti Patterns - wiki article written by Petka Antonov, creator of Bluebird (A+ implementation). It's explicitly said that this is wrong.
So my second question is - is it so? If yes - why? And what's the best way?
Thanks a lot for reading this, I hope someone will spent time to answer it for me :) I should add that I didn't want to depend too much on frameworks, so Sequelize and Bluebird are just things that I ended up working with. My problem is with Promises as a global, not with this particular frameworks.
Please ask only a single question per post :-)
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
No. They just do different things, but once you know that you can choose the appropriate one.
How to use promisify and what are the benefits of it?
I think the Bluebird Promisification docs explain this pretty well - it's used to convert a callback api to one that returns promises.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error?
Yes, throwing an error is totally fine. Inside a then callback, you can throw and it will be caught automatically, resulting to the rejection of the result promise.
You can also use return Promise.reject(new Error(…));; both will have absolutely the same effect.
A friend of mine helped me out and I used a new promise to fix things up, like this: […]
No. You really shouldn't use that. Just use then and throw or return a rejected promise in there.
But if I miss the catch statement - my whole app will blow!
No, it won't. Notice that .catch() method is not a try catch statement - the error will already be caught where your then callback was invoked. It is then only passed to the catch callback as an argument, the .catch() call is not required to capture exceptions.
And when you miss .catch(), your app won't blow. All what happens is that you have a rejected promise laying around, the rest of your app won't be affected by this. You will get an unhandled rejection event, which can be dealt with globally.
Of course, that should not happen; every promise chain (not every promise instance) should be ended with a .catch() that handles errors appropriately. But you definitely don't need .catch() in every helper function, when it returns a promise to somewhere else then errors will usually be handled by the caller.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
And if you use return codes to signal errors instead of exceptions, missing the check will cause subtle bugs and erratic behavior. Forgetting to handle errors is a bug, but having your app blow up in such an unfortunate case is more often better than letting it continue in corrupted state.
Debugging forgotten error code check that is only apparent by the application behaving weirdly at some unrelated-to-the-source-of-error place is easily many orders of magnitude more expensive than debugging a forgotten catch because you have the error message, source of error and stack trace. So if you want productivity in typical application development scenario, exceptions win by a pretty huge margin.
.then(success, fail)
This is usually signal that you are using promises as glorified callbacks where the callbacks are simply passed separately. This is obviously an anti-pattern as you will not get any benefits from using promises in this way. If that's not the case, even then you are just making your code slightly less readable compared to using .catch(), similar to how method(true, true, false) is much less readable than method({option1: true, option2: true, option3: false}).
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Most modules only expose a callback interface, promisify turns a callback interface automatically into a promise interface. If you are writing such code manually (using deferred or new Promise), you are wasting your time.
So my second question is - is it so? If yes - why? And what's the best way?
The best way is to chain the promise:
function findFirst() {
return Entity.find(1).tap(function(result) {
if (!result) throw new Error("no result");
});
}
It's better because it's simpler, shorter, more readable and far less error-prone way to do exactly the same thing.