Clean Code Applied to JavaScript — Part V. Exceptions
In software development, exception handling is an integral part of high-quality code so that we can effectively control unexpected situations and unimplemented logic in the program. However, developers sometimes confuse exception handling with the process handling of software.
Exceptions should be used to handle situations that are out of control or undeveloped in the software, rather than return as business logic does and then handle the situation in a branch process of the business.
In this article, we will provide some advice on exception handling to improve code quality.
Use exceptions instead of return codes
If the programming language supports exception handling, it is recommended that you use exception handling first. This seems simple enough, but it’s not. Because, some development languages may not support exception handling; On the other hand, some developers are not aware of the benefits of exception handling and ignore this feature instead of using it. However, using exceptions to handle errors is more concise and efficient than using return codes to check for errors.
In the first section below, you define a class, and then in the method it implements, you use the if statement to check if there are any illegal return codes in the return value. The problem with this is that the caller needs to do error checking immediately after receiving the return value of the function, which complicates the caller’s code, and can cause problems if the error checking is forgotten. We should leave the tedious and complicated work to the exception handling of the language itself. In the second piece of code, exception handling is used to isolate the two different business logic. This code has three advantages:
- Isolate business logic and error handling. They are two different issues that must be handled and treated separately
- The code is less redundant and easier to read
- The responsibility for handling program exceptions rests with the programming language
// Dirty
class Laptop {
sendShutDown() {
const deviceID = getID(DEVICE_LAPTOP);
if(deviceID ! == DEVICE_STATUS.INVALID) {const laptop = DB.findOne(deviceID);
if(laptop.getStatus() ! == DEVICE_SUSPENDED) { pauseDevice(deviceID); clearDeviceWorkQueue(deviceID); closeDevice(deviceID); }else {
logger.log('Device suspended. Unable to shut down'); }}else {
logger.log('Invalid handle for: '+ DEVICE_LAPTOP.toString()); }}}Copy the code
// Clean
/* The code is better because the algorithm and error handling, are now separated. */
class Laptop {
sendShutDown() {
try {
tryToShutDown();
} catch(error) { logger.log(error); }}tryToShutDown() {
const deviceID = getID(DEVICE_LAPTOP);
const laptop = DB.findOne(deviceID);
pauseDevice(deviceID);
clearDeviceWorkQueue(deviceID);
closeDevice(deviceID);
}
getID(deviceID){...throw new DeviceShutDownError('Invalid handle for: '+ deviceID.toString()); . }}Copy the code
Don’t ignore exception handling
Please don’t be an ostrich. An ostrich will bury its head in the ground when it is in danger. When it comes to exception handling, we can’t be ostriches and pretend nothing happened every time we catch an error. It makes no sense not to do anything about the errors caught.
However, if we just use console.log or system.out.println as an error, it also means that nothing is done. It is very dangerous to watch an exception happen in practice without doing anything about it. Because these anomalies are usually caused by unexpected circumstances, they can reveal many problems that are not readily apparent. Therefore, do not ignore the handling of exceptions.
Translator:… Catch is a great way to keep JavaScript code from going blank when something goes wrong, but it’s scary not to do anything about caught exceptions. In practice, pages are often connected to an error reporting tool (such as Sentry), and when the JavaScript code on the page fails, the error message is reported to the developer, who can handle the problem in a timely manner. Once exception catching is done in the code, the reporting tool will not be able to proactively catch the error. If the caught exception is not handled at this time, the error logic of the code will not be discovered in time, so it is good to log even the exception only.
In the first code, which junior developers often do, there is no effective handling of exceptions. In the second example, the necessary processing was done after the error was caught, and while it took time and effort, it was well worth it.
try {
functionThatMightThrow();
} catch (error) {
console.log(error);
}
Copy the code
try {
functionThatMightThrow();
} catch (error){
console.error(error);
notifyUserOfError(error);
reportErrorToService(error);
}
Copy the code
Don’t ignore Promise reject
In JavaScript, you need to pay special attention to the asynchronous processing tool Promise, as well as the exception logic thrown by a Promise through reject.
In this example, we see a similar example to the previous one, but it uses a Promise.
getData()
.then(data= > functionThatMightThrow(data))
.catch(error= > console.log);
Copy the code
getData()
.then(data= > functionThatMightThrow(data))
.catch(error= > {
console.log(error);
notifyUserOfError(error);
reportErrorToService(error);
});
Copy the code
Defining the exception Hierarchy
Each programming language defines some basic exceptions, such as NullPointerException or ArrayIndexOutOfBoundsException, these exceptions are not involved in our business logic. There is no point in using these exceptions to control errors in our code, because our code is modeling business logic. Therefore, we have to create our own hierarchy of exceptions that relate to our business logic and are triggered when an unexpected situation occurs in our business logic.
In the following example, two exception types are created, a UserException and an AdminException, that occur on both types of users rather than on data structures. We now define the business logic of the code.
In fact, the definition of these two exceptions is too general. We can define UserRepeatException, UserNotFoundException…
We need to make exceptions semantically, otherwise it will be difficult to analyze the errors even if they are caught.
export class UserException extends Error {
constructor(message) {
super(`User: ${mesage}`); }}export class AdminException extends Error {
constructor(message) {
super(`Admin: ${message}`); }}// Client code
const id = 1;
const user = this.users.find({ id });
if(user){
throw new UserException('This user already exists');
}
Copy the code
Provide the context for the exception
While we can use stack traces and chaining calls to see when an exception occurs, it is still very difficult to analyze. Therefore, it is necessary to add comments and information to the exception. For example, where an exception is caught, add a message explaining our intent. But please don’t use complicated language to explain. It is important to note that the information we provide should not be visible to the end-user. Therefore, we should properly manage the exception information, so that the information is not displayed in the user interface, it would be better.
If we define an exception hierarchy, we also provide context for the exception.
conclusion
In this article, we offer some advice on exception handling. Exception handling is an essential part of high-quality software development, but in many cases they are ignored or used incorrectly just to keep the code flow error-free and redirect to the correct flow.
Countless examples show that if a programming language provides exception handling, we must use it to handle exceptions so that we can focus on the logic of the business itself.
Finally, let’s review the main points of this article:
- Use exceptions instead of return codes
- Don’t ignore exception handling
- Don’t ignore Promise reject
- Defining the exception Hierarchy
- Provide the context for the exception
Related articles
- The Way to Clean JavaScript code – Complex judgment
- How to clean JavaScript code – Comments
- How to clean JavaScript code – Function section
- Simple practice for writing clean React code
- Simple tips for writing a concise React component
Click to follow the official account “KooFE Front-end Team”