Appearance
Error Handling
When executing JavaScript code, errors can occur in certain situations.
There are two types of errors: one is due to incorrect logic in the program, which leads to exceptions during code execution. For example:
javascript
let s = null;
let len = s.length; // TypeError: null does not have a length property
For this type of error, the program needs to be fixed.
The other type of error occurs when the program encounters unpredictable exceptions during execution, such as network disconnections, attempting to read a nonexistent file, or lacking operation permissions.
For these types of errors, we need to handle them and possibly provide feedback to the user.
Error handling is a crucial consideration in program design. In low-level languages like C, errors are returned through error codes:
c
int fd = open("/path/to/file", O_RDONLY);
if (fd == -1) {
printf("Error when opening file!");
} else {
// TODO
}
Returning errors through error codes requires an agreement on what constitutes a correct return value and what constitutes an error return value. The open() function above stipulates that returning -1 indicates an error.
Clearly, using error codes to represent errors is very inconvenient when writing programs.
Therefore, high-level languages typically provide more abstract error handling logic such as try ... catch ... finally, and JavaScript is no exception.
try ... catch ... finally
When handling errors using try ... catch ... finally, our code looks like this:
javascript
let r1, r2, s = null;
try {
r1 = s.length; // This will cause an error
r2 = 100; // This statement will not execute
} catch (e) {
console.log('An error occurred: ' + e);
} finally {
console.log('finally');
}
console.log('r1 = ' + r1); // r1 should be undefined
console.log('r2 = ' + r2); // r2 should be undefined
After running, the output will indicate an error similar to “An error occurred: TypeError: Cannot read property 'length' of null.”
Let’s analyze the execution flow of using try ... catch ... finally.
When the code block is wrapped in try { ... }, it indicates that an error may occur during the execution of this part of the code. Once an error occurs, execution of subsequent statements will cease, and control will jump to the catch block. The code in catch (e) { ... } is the error handling code, where the variable e represents the caught error. Finally, regardless of whether an error occurred, the finally block will always be executed.
So, when an error occurs, the execution flow is as follows:
- Execute the code in try { ... };
- When reaching the erroneous statement, subsequent statements will not continue, and control will move to catch (e) { ... };
- Finally, execute finally { ... }.
When no error occurs, the execution flow is:
- Execute the code in try { ... };
- Since no error occurred, catch (e) { ... } will not be executed;
- Finally, execute finally { ... }.
Finally, note that catch and finally do not have to both be present. That is to say, there are three forms of the try statement:
- Complete try ... catch ... finally:
javascript
try {
...
} catch (e) {
...
} finally {
...
}
- Only try ... catch, without finally:
javascript
try {
...
} catch (e) {
...
}
- Only try ... finally, without catch:
javascript
try {
...
} finally {
...
}
Error Types
JavaScript has a standard Error object that represents errors, along with derived error objects like TypeError and ReferenceError. When handling errors, we can access the error object through the variable e caught in catch(e):
javascript
try {
...
} catch (e) {
if (e instanceof TypeError) {
alert('Type error!');
} else if (e instanceof Error) {
alert(e.message);
} else {
alert('Error: ' + e);
}
}
Using the variable e is a common practice, but it can be named differently, such as catch(ex).
Throwing Errors
Programs can also throw an error manually, causing control to jump directly to the catch block. Throwing an error is done using the throw statement.
For example, the following code prompts the user to enter a number. The input received is actually a string, which is then converted to an integer using parseInt(). When the user inputs an invalid value, we throw an error:
javascript
let r, n, s;
try {
s = prompt('Please enter a number');
n = parseInt(s);
if (isNaN(n)) {
throw new Error('Input error');
}
// Calculate the square:
r = n * n;
console.log(n + ' * ' + n + ' = ' + r);
} catch (e) {
console.log('An error occurred: ' + e);
}
In reality, JavaScript allows throwing any object, including numbers and strings. However, it is still best to throw an Error object.
Finally, when catching errors with catch, it is essential to include error handling statements:
javascript
let n = 0, s;
try {
n = s.length;
} catch (e) {
console.log(e);
}
console.log(n);
Even just printing the error should not be omitted:
javascript
let n = 0, s;
try {
n = s.length;
} catch (e) {
}
console.log(n);
Failing to execute any statements when catching an error makes it unclear whether an error occurred during program execution.
When handling errors, do not simply use alert() to display the error to the user. Using alert() in tutorial code is for demonstration purposes.