It is very common languages such as java and c # to create custom exceptions, to differentiate error situations from one another. In JS there is the error object and several other types, but they are for very limited uses.
For that reason there is also the possibility of creating our types of exceptions. To simulate that there is a typing for them. We can do this in a 100% OOP or functional way.
Using CLASS:
We can create custom exceptions for our programs, in this case using OOP class in JS.
Demostration code
class ValidationError extends Error {
constructor(message) {
super(message)
this.name = 'VALIDATION_ERROR'
this.message = message
}
}
class PermissionError extends Error {
constructor(message) {
super(message)
this.name = 'PERMISSION_ERROR'
this.message = message
}
}
class ExecutionError extends Error {
constructor(message) {
super(message)
this.name = 'EXECUTION_ERROR'
this.message = message
}
}
module.exports = {
ValidationError,
PermissionError,
DatabaseError
}
How to use it?
function myThrow(input) {
if (!input)
throw new ExecutionError('A execution error');
return input
}
Using FUNCTIONS:
We can create custom exceptions using a functional programming style.
Demostration code
const ValidationError = (message)=>({
error: new Error(message),
code: 'VALIDATION_ERROR'
});
const PermissionError = (message)=>({
error: new Error(message),
code: 'PERMISSION_ERROR'
});
const ExecutionError = (message)=>({
error: new Error(message),
code: 'EXECUTION_ERROR'
});
Example
const {
ValidationError,
PermissionError,
DatabaseError
} = require('./exceptions.js');
function myThrow(input) {
if (!input)
throw ExecutionError('A execution error');
return input
}
Extending the exception methods:
"Serializing Error objects", I use function to can use the constructor later.
//Define exceptions.
function MyError(message){
const internal = {
error: new Error(message),
code: 'MY_CUSTOM_ERROR'
};
return {
...internal,
toJSON:()=>({
code: internal.code,
stack: internal.error.stack,
message
})
}
}
MyError.prototype = Object.create(Error.prototype);
Example:
//Launch
throw new MyError('So bad configuration');
//Capturing.
try{
//.....
throw new MyError('So bad configuration');
//.....
} catch(err){
console.log('Error',err.toJSON());
}
Real life examples:
Having read the previous examples, it is time to find real examples of exceptions that we could create. I am going to propose some that I have used in past projects.
HTTP error codes
- Bad Request
- Unauthorized
- Not Found
- Internal Server Error
- Bad Gateway
//Definif exceptions.
const BadRequest = ()=>({
message: 'Bad Request',
code:400
});
const Unauthorized = ()=>({
message: 'Unauthorized',
code:401
});
const NotFound = ()=>({
message: 'Not Found',
code: 404
});
const InternalServerError = ()=>({
message: 'Internal Server Error',
code: 500
});
const BadGateWay = ()=>({
message: 'Bad Gateway',
code: 502
});
//Define exceptions map.
const exceptionMap = {
502: BadGateway,
500: InternalServerError,
404: NotFound,
401: Unauthorized,
400: BadRequest
};
//Using it.
const getUser = async(userId)=>{
//Make request, this line is just an example, use a real rest client.
const response = await fetch('http://user.mock/'+userId);
//Get httpcode.
const {
status,
body
} = response;
//Get the exception.
const exception = exceptionMap[status];
if (!exception)
throw exception();
else
return body;
}
Bussiness operations
- Loan rejected
- Loan exced
- Loan pending
//We have this custom exceptions
const LoanRejected = (motive, clientId)=>({
message: 'The loan was rejected',
code:'LOAN_REJECTED',
motive,
clientId
});
const LoanExced = (clientId)=>({
message: 'The loan ammount exced the limits',
code:'LOAN_EXCED',
clientId
});
const LoanPending = ()=>({
message: 'The client has a loan pending for payment',
code:'LOAN_PENDING'
});
//I simulate a loan process.
const processate = async(clientId,ammount)=>{
const status = await getLoanStatus(clientId,ammount);
//Detect status to reject the loan.
if (status.code=='REJECTED')
throw LoanRejected('Status not ready to calc',clienId);
if (status.code=='UNAVAILABLE')
throw LoanRejected('Clien banned',clienId);
if (status.code=='EXCED')
throw LoanExced();
//If the client has debts.
if (status.code=='PENDING')
throw LoanPending();
const loanId = await createLoan(clientId);
return loanId;
}
//And we detect the type of exceptions.
const loanFlow = async (clientId,ammount)=>{
try{
const loanId = procesate(clientId,ammount);
console.log('New loan create',loanId);
} catch(err){
if (err.code.includes['LOAN_REJECTED','LOAN_EXCED','LOAN_PENDING'])
console.log('Loan rejected!!');
else
console.log('Problem in process try again later...');
}
}
Top comments (13)
You don't need explicit call this line
Did you mean !!exception?
Good article!
It's easy to just throw error and use catch to handle the exception, but ideally you shouldn't use throw very often as it's a huge drawback on performance.
The way I've always heard, and continue to follow, exceptions are for exceptional conditions. They shouldn't be for logic flow.
In a good many of my projects (all languages), there's plenty of throws, but almost no catches at all, except maybe at or near the top level. In the case of errors, I want things to blow up. If the db goes down, there's not much I can do. If I have a bug that results in a throw, it's noisy and I have a nice stack trace right to it, fail early and noisy.
Well I'm using this way in a very massive production project and we don't lose Performance. Do you have a link that supports this or what you say or experience to tell?
try-catch will impact your performance only if you actually throw exceptions.
Examples:
Conclusion:
I'm not saying that you should not use try-catch at all, but like in the examples above actually throwing an exception can be easily avoided.
Nice, thanks for the links provides very useful information. I will be investigating about this.
Can you explain this in more detail? I guess throwing a lot of exceptions frequently may introduce performance issues, but not sure why. Is it because of the stack tracing?
Yes, you are right. It's because of the stack tracing. As it needs to build your stack trace and it walks through the call stack to collect all required information like method names, line numbers etc.
Maybe the problem Is hoy don't catch the exceptoins very well.Seriously, it's the 1st time I've heard that throwing exceptions loses Performance. but I want to see how you base it, I don't say no but I am working like this on a project and we didn't lose performance
Good article.
Thanks!
Loved it.