By Valentino Gagliardi
Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.
Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.
What are errors in programming
It wasn’t always smooth sailing. In particular, in some cases, we may want to stop the program or notify the user when something bad happens.
Such as:
- The program attempts to open a file that does not exist,
- Network connection down
- The user entered an invalid character
In cases like these, we can write our own custom errors to manage, or let the engine define them for us. With an error definition, we can notify the user with a message or stop the execution of the program.
What’s wrong with JavaScript
An error in JavaScript is an object. To create an Error in JS, you can use an Error object like this:
Const err = new Error(' Hoho, something is wrong! ')Copy the code
You can also omit the new keyword:
Const err = Error(' Hoho, something is wrong! ')Copy the code
Create, error object has three properties:
-
Message: String with an error message
-
Name: Wrong type
-
Stack: Stack trace of function execution
For example, we create an error using a TypeError object, where message is the created incoming character number and name is “TypeError” 😀
Const wrongType = TypeError(" Hoho, something is wrong! ") ) wrongType.message // "Hoho, something seems to be wrong!" wrongType.name // "TypeError"Copy the code
Many types of errors in JavaScript
There are many types of errors in JavaScript 😱, such as:
- Error
- EvalError
- InternalError
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URIError
Remember that all of these error types are actual constructors, meaning that a new error object is returned.
In our code, we mainly use the two most common types, Error and TypeError, to create our own Error object 😳.
Most of the time, most errors will come directly from a JavaScript engine, such as InternalError or SyntaxError.
TypeError is raised if you reassign to a variable declared by const.
// // TypeError: Assignment to constant variableCopy the code
SyntaxError is a keyword typo, as shown in the following example:
va x = '33';
// SyntaxError: Unexpected identifier
Copy the code
Or, when using keywords in the wrong place, such as await and async:
function wrong(){
await 99;
}
wrong();
// SyntaxError: await is only valid in async function
Copy the code
Another example of TypeError is operating on a DOM element that does not exist on a page.
Uncaught TypeError: button is null
Copy the code
In addition to these built-in errors, there are:
-
DOMException
-
DOMError, now deprecated, is no longer in use.
DOMException is a series of errors associated with Web API. They are thrown when we perform stupid operations in the browser, such as:
document.body.appendChild(document.cloneNode(true));
Copy the code
Results:
Uncaught DOMException: Node.appendChild: May not add a Document as a child
Copy the code
What is an exception?
Most developers think of errors and exceptions as the same thing. In fact, an error object becomes an exception only when it is thrown.
To throw an exception in JavaScript, we use the throw keyword to throw the error:
Const wrongType = TypeError(" Hoho, something is wrong! ") ) throw wrongType;Copy the code
Short form:
Throw TypeError(" Hoho, something seems to be wrong!" )Copy the code
or
Throw New TypeError(" Hoho, something seems to be wrong! ") )Copy the code
It is unlikely that asynchrony will be thrown outside of a function body or condition. Consider the following example:
function toUppercase(string) { if (typeof string ! == "string") {throw TypeError(" Hoho, there seems to be something wrong!" ); } return string.toUpperCase(); }Copy the code
Here we check if the function argument is a string. If not, we throw an exception. Technically, anything can be thrown in JavaScript, not just error objects
throw Symbol(); throw 33; throw "Error!" ; throw null;Copy the code
However, it’s best to avoid these things: always throw the correct error object instead of some primitive type.
This helps with consistent error handling in your code. Other members can expect to access error. Message or error. Stack on the error object to know the source of the error.
Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.
What happens when we throw an exception?
Exceptions are like a rising elevator: once you throw one, it bubbles up in the program stack unless it’s caught somewhere.
Consider the following code:
function toUppercase(string) { if (typeof string ! == "string") {throw TypeError(" Parameter type needs to be string"); } return string.toUpperCase(); } toUppercase(4);Copy the code
Running the code will see it on the console:
Uncaught TypeError: Wrong type given, expected a string
toUppercase http://localhost:5000/index.js:3
<anonymous> http://localhost:5000/index.js:9
Copy the code
You can see the exact row where the error occurred.
This report is a stack trace that helps track problems in your code. Stack trace from bottom to top:
toUppercase http://localhost:5000/index.js:3
<anonymous> http://localhost:5000/index.js:9
Copy the code
In addition to seeing this stack trace in the browser console, you can also view it through the Stack property of the error object.
If the exception is not caught, that is, the programmer does nothing to catch it, the program crashes.
When and where exceptions are caught in your code depends on the specific use case.
For example, we might want to pass an exception on the stack to crash the program completely. This happens when it is safer to stop the program in error than to process invalid data.
Next, let’s look at error and exception handling in synchronous and asynchronous JavaScript.
Error handling in synchronization
Synchronous code is simple in most cases, and so is its error handling.
Error handling of normal functions
Synchronized code is executed in the same order as it is written. Let’s look at the previous example:
function toUppercase(string) { if (typeof string ! == "string") {throw TypeError(" Parameter type needs to be string"); } return string.toUpperCase(); } toUppercase(4);Copy the code
Here, the engine calls and executes the toUppercase. All of this happens simultaneously. To catch exceptions thrown by synchronous functions, we can use try/catch/finally:
try {
toUppercase(4);
} catch (error) {
console.error(error.message);
} finally {
}
Copy the code
Try /catch/finally is a synchronous construct, but it can also catch exceptions that occur asynchronously.
Use generator functions to handle errors
A generator function in JavaScript is a special kind of function. In addition to providing a two-way communication channel between its internal scope and the consumer, it can be paused and resumed at will.
To create a generator function, we put a * after the function keyword:
function* generate() {
//
}
Copy the code
We can use yield to return values within functions:
function* generate() {
yield 33;
yield 99;
}
Copy the code
The return value of a generator function is an iterator object. To extract values from the generator, we can use two methods:
- use
next()
methods - through
for... of
traverse
To get a value from the generator, we can do something like this:
function* generate() { yield 33; yield 99; } const go = generate(); const firstStep = go.next().value; // 33 const secondStep = go.next().value; / / 99Copy the code
Synthesizers can also work in other ways: they can receive values and exceptions returned by the caller.
In addition to next(), iterator objects returned from generators also have throw() methods. Using this approach, we can stop the program by injecting an exception into the generator
function* generate() { yield 33; yield 99; } const go = generate(); const firstStep = go.next().value; // 33 go.throw(Error(" I'm going to end you!" )); const secondStep = go.next().value; // An exception will be thrownCopy the code
To catch this error, use try/catch/finally ina generator function:
function* generate() { try { yield 33; yield 99; } catch (error) { console.error(error.message); }}Copy the code
This is an example of using for… Of to get the value in the generator function:
function* generate() { yield 33; yield 99; Throw Error(" I'm going to end you!" )} try {for (const value of generate()) {console.log(value)}} catch (error) {console.log(error. Message)} /* 33 99 I will finish you! * /Copy the code
Error handling in asynchrony
JavaScript is synchronous in nature and is a single-threaded language.
Host environments such as browser engines use a number of Web apis, enhancing JS to interact with external systems and handle I/O binding operations.
Asynchronous operations in the browser include timer related functions, events, and promises.
Error handling in asynchrony is different from synchronous error handling. Let’s look at some examples.
Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.
Timer error handling
Consider the following code snippet:
function failAfterOneSecond() { setTimeout(() => { throw Error("Something went wrong!" ); }, 1000); }Copy the code
This function throws an exception after about 1 second. What is the correct way to handle this exception?
The following methods don’t work:
function failAfterOneSecond() { setTimeout(() => { throw Error("Something went wrong!" ); }, 1000); } try { failAfterOneSecond(); } catch (error) { console.error(error.message); }Copy the code
We know that try/catch is synchronous and setTimeout is asynchronous. By the time the setTimeout callback is executed, the try/catch is long gone, so the exception cannot be caught.
They are on two different tracks:
Track A: --> try/catch
Track B: --> setTimeout --> callback --> throw
Copy the code
If you can get the program to run, move the try/catch inside setTimeout. But that doesn’t make much sense, and we’ll use promises to solve these kinds of problems later.
Error handling in the event
DOM event operations (listening and firing) are defined in the EventTarget interface. The Element node, the Document node, and the Window object all deploy this interface. In addition, XMLHttpRequest, AudioNode, AudioContext, and other browser built-in objects also deploy this interface. The interface consists of three methods, addEventListener and removeEventListener to bind and remove listener functions, and dispatchEvent to trigger events.
The error handling mechanism for DOM events follows the same scheme as any asynchronous Web API.
Consider the following example:
const button = document.querySelector("button"); button.addEventListener("click", function() { throw Error("Can't touch this button!" ); });Copy the code
In this case, an exception is thrown immediately after the button is clicked. How do we catch it? The following does not work and does not prevent crashes:
const button = document.querySelector("button"); try { button.addEventListener("click", function() { throw Error("Can't touch this button!" ); }); } catch (error) { console.error(error.message); }Copy the code
Like setTimeout, addEventListener is executed asynchronously.
Track A: --> try/catch
Track B: --> addEventListener --> callback --> throw
Copy the code
Move the try/catch into the addEventListener if you can get the program to run. But that doesn’t make much sense, and we’ll use promises to solve these kinds of problems later.
Onerror?
HTML elements have many event handlers, such as onClick, onMouseEnter, onchange, and of course onError.
The onError event handler is raised whenever the IMG tag or script tag encounters a resource that does not exist.
Consider the following example:
. <body> <img src="nowhere-to-be-found.png" alt="So empty!" > </body> ...Copy the code
When the file does not exist, the console will report the following error:
GET http://localhost:5000/nowhere-to-be-found.png [HTTP / 1.1 404 Not Found 3 ms]Copy the code
In JS, we can catch this error with onError:
const image = document.querySelector("img");
image.onerror = function(event) {
console.log(event);
};
Copy the code
A better way:
const image = document.querySelector("img");
image.addEventListener("error", function(event) {
console.log(event);
});
Copy the code
This approach is useful for some cases where the requested resource is lost, but onError and throw have nothing to do with try/ CATHC.
Use promises to handle errors
To demonstrate the Promise processing, let’s go back to the original example:
function toUppercase(string) { if (typeof string ! == "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } toUppercase(4);Copy the code
To throw an exception, we can use promise. reject and promise.resolve:
function toUppercase(string) { if (typeof string ! == "string") { return Promise.reject(TypeError("Wrong type given, expected a string")); } const result = string.toUpperCase(); return Promise.resolve(result); }Copy the code
Because you use promises, you can use then to receive returned content or catch to catch errors.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message));
Copy the code
The result is as follows:
Wrong type given, expected a string
Copy the code
In addition to then and catch, promises also have finally methods, which are similar to finally in try/catch.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message))
.finally(() => console.log("Run baby, run"));
Copy the code
Promise, error, and throw
Promise. Reject makes it easy to throw an error:
Promise.reject(TypeError("Wrong type given, expected a string"));
Copy the code
In addition to promise.reject, we can also exit a Promise by throwing an exception.
Consider the following example:
Promise.resolve("A string").then(value => {
if (typeof value === "string") {
throw TypeError("Expected a number!");
}
});
Copy the code
To stop exception propagation, we use catch as usual:
Promise.resolve("A string") .then(value => { if (typeof value === "string") { throw TypeError("Expected a number!" ); } }) .catch(reason => console.log(reason.message));Copy the code
This pattern is common in FETCH:
fetch("https://example-dev/api/") .then(response => { if (! response.ok) { throw Error(response.statusText); } return response.json(); }) .then(json => console.log(json));Copy the code
Here you can use catch to intercept exceptions. If we fail, or decide not to catch it, the exception is free to bubble up on the stack.
Use promises to handle exceptions in timers
Exceptions thrown from callbacks cannot be caught using timers or events.
function failAfterOneSecond() { setTimeout(() => { throw Error("Something went wrong!" ); }, 1000); } // DOES NOT WORK try { failAfterOneSecond(); } catch (error) { console.error(error.message); }Copy the code
The solution is to use promises:
function failAfterOneSecond() { return new Promise((_, reject) => { setTimeout(() => { reject(Error("Something went wrong!" )); }, 1000); }); }Copy the code
With Reject, we launch a Promise reject that carries an error object.
At this point, we can use catch to handle exceptions:
failAfterOneSecond().catch(reason => console.error(reason.message));
Copy the code
Use promise.all to handle errors
The promise.all (iterable) method returns an instance of a Promise that is resolved when all promises in iterable arguments are “resolved” or when the arguments do not contain the Promise;
const promise1 = Promise.resolve("All good!" ); const promise2 = Promise.resolve("All good here too!" ); Promise.all([promise1, promise2]).then((results) => console.log(results)); // [ 'All good!', 'All good here too!' ]Copy the code
If the promise parameter has a failed (Rejected), this instance calls back failed (Reject), which is the result of the first failed promise.
const promise1 = Promise.resolve("All good!" ); const promise2 = Promise.reject(Error("No good, sorry!" )); const promise3 = Promise.reject(Error("Bad day ..." )); Promise.all([promise1, promise2, promise3]) .then(results => console.log(results)) .catch(error => console.error(error.message)); // No good, sorry!Copy the code
Also, finally is executed no matter how the function is run as a result of promise.all:
Promise.all([promise1, promise2, promise3]) .then(results => console.log(results)) .catch(error => console.error(error.message)) .finally(() => console.log("Always runs!" ));Copy the code
Use promise.any to handle errors
Promise.any() (Firefox > 79, Chrome > 85) receives a Promise iterable and returns the Promise that succeeded as soon as one of the promises succeeds. If none of the promises in the iterable succeed (i.e. all Promises fail/reject), return an instance of the failed promise and AggregateError type, which is a subclass of Error and is used to group single errors together. Essentially, this method is the opposite of promise.all ().
const promise1 = Promise.reject(Error("No good, sorry!" )); const promise2 = Promise.reject(Error("Bad day ..." )); Promise.any([promise1, promise2]) .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log("Always runs!" ));Copy the code
Here, we use catch to handle errors, and the output looks like this:
AggregateError: No Promise in Promise.any was resolved
Always runs!
Copy the code
The AggregateError object has the same attributes as the base Error, plus the errors attribute:
//
.catch(error => console.error(error.errors))
//
Copy the code
This property is an array of each individual error produced by Reject
[Error: "No good, sorry!, Error: "Bad day ..."]
Copy the code
Use promise.race to handle errors
The promise.race (iterable) method returns a Promise that is resolved or rejected once a Promise in the iterator is resolved or rejected.
const promise1 = Promise.resolve("The first!" ); const promise2 = Promise.resolve("The second!" ); Promise.race([promise1, promise2]).then(result => console.log(result)); // The first!Copy the code
This shows that the first Promise executes more than the second line. What about situations that involve rejection?
const promise1 = Promise.resolve("The first!" ); const rejection = Promise.reject(Error("Ouch!" )); const promise2 = Promise.resolve("The second!" ); Promise.race([promise1, rejection, promise2]).then(result => console.log(result) ); // The first!Copy the code
What if you put reject first?
const promise1 = Promise.resolve("The first!" ); const rejection = Promise.reject(Error("Ouch!" )); const promise2 = Promise.resolve("The second!" ); Promise.race([rejection, promise1, promise2]) .then(result => console.log(result)) .catch(error => console.error(error.message)); // Ouch!Copy the code
Use promise.allSettled to handle errors
The promise.allSettled () method returns a Promise after all the given promises have fulfilled or rejected, with an array of objects each representing the corresponding Promise result.
Consider the following example:
const promise1 = Promise.resolve("Good!" ); const promise2 = Promise.reject(Error("No good, sorry!" )); Promise.allSettled([promise1, promise2]) .then(results => console.log(results)) .catch(error => console.error(error)) .finally(() => console.log("Always runs!" ));Copy the code
We passed Promises. allSettled an array of two promises: one settled and one rejected.
In this case catch is never executed, finally is always executed.
[
{ status: 'fulfilled', value: 'Good!' },
{
status: 'rejected',
reason: Error: No good, sorry!
}
]
Copy the code
Use async/await to handle errors
For simplicity, we use the previous synchronization function toUppercase and convert it to an asynchronous function by placing async before the function keyword
async function toUppercase(string) { if (typeof string ! == "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); }Copy the code
As long as you precede async, the function returns a Promise. This means that we can do then, catch, and finally operations after a function call
async function toUppercase(string) { if (typeof string ! == "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } toUppercase("abc") .then(result => console.log(result)) .catch(error => console.error(error.message)) .finally(() => console.log("Always runs!" ));Copy the code
When an exception is thrown from an async function, we use a catch to catch it.
Most importantly, in addition to this approach, we can also use try/catch/finally, just as we did with synchronous functions.
async function toUppercase(string) { if (typeof string ! == "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } async function consumer() { try { await toUppercase(98); } catch (error) { console.error(error.message); } finally { console.log("Always runs!" ); } } consumer();Copy the code
Output:
Wrong type given, expected a string
Always runs!
Copy the code
Use async generators to handle errors
Async generators in JavaScript are generator functions that generate Promises instead of simple values.
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!" ); // Promise.reject }Copy the code
Based on Promise, the same rules apply here for error handling. In an asynchronous generator, a throw triggers a Promise’s Reject, which we can intercept using a catch.
To use the Promise of an asynchronous generator, we can do this:
- Then method
- Asynchronous traversal
We know from above that after two calls to yield, an exception will be thrown the next time:
const go = asyncGenerator();
go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.next().catch(reason => console.error(reason.message));
Copy the code
Output result:
{ value: 33, done: false }
{ value: 99, done: false }
Something went wrong!
Copy the code
The other is to use asynchronous traversal with for await… of:
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!" ); // Promise.reject } async function consumer() { for await (const value of asyncGenerator()) { console.log(value); } } consumer();Copy the code
With async/await we can use try/catch to catch exceptions:
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!" ); // Promise.reject } async function consumer() { try { for await (const value of asyncGenerator()) { console.log(value); } } catch (error) { console.error(error.message); } } consumer();Copy the code
Output result:
33
99
Something went wrong!
Copy the code
An iterator object returned from an asynchronous generator function also has a throw() method, much like its synchronous counterpart. Calling throw() on an iterator object here does not raise an exception, but is rejected by Promise
async function* asyncGenerator() { yield 33; yield 99; yield 11; } const go = asyncGenerator(); go.next().then(value => console.log(value)); go.next().then(value => console.log(value)); go.throw(Error("Let's reject!" )); go.next().then(value => console.log(value)); // value is undefinedCopy the code
To handle this externally, we can do:
go.throw(Error("Let's reject!" )).catch(reason => console.error(reason.message));Copy the code
Error handling in Node
Synchronization error handling in Node
Synchronization error handling in Node.js isn’t that different from what you’ve seen so far. For synchronization, try/catch/finally works fine.
Asynchronous error handling in Node.js: callback mode
For asynchronous code, Node.js uses two main methods:
- The callback mode
- event emitters
In callback mode, the asynchronous Node.js API accepts a function that is processed through an event loop and executed immediately when the call stack is empty.
Consider the following code:
const { readFile } = require("fs");
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) console.error(error);
// do stuff with the data
});
}
Copy the code
As we can see, the error is handled using a callback:
//
function(error, data) {
if (error) console.error(error);
// do stuff with the data
}
//
Copy the code
If any errors are caused by reading a given path using fs.readfile, we get an error object.
At this point, we can:
- Simply type out the object error
- Throw an error
- Pass the error to another callback
We can throw an exception
const { readFile } = require("fs");
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) throw Error(error.message);
// do stuff with the data
});
}
Copy the code
However, as with events and timers in the DOM, this exception will crash the program. Catching it by try/catch does not work:
const { readFile } = require("fs");
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) throw Error(error.message);
// do stuff with the data
});
}
try {
readDataset("not-here.txt");
} catch (error) {
console.error(error.message);
}
Copy the code
If we don’t want the program to crash, passing the error to another callback is the preferred method:
const { readFile } = require("fs");
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) return errorHandler(error);
// do stuff with the data
});
}
Copy the code
ErrorHandler here is, as its name implies, a simple function for error handling:
function errorHandler(error) {
console.error(error.message);
// do something with the error:
// - write to a log.
// - send to an external logger.
}
Copy the code
Asynchronous error handling in Node.js: Event emitters
Most of the work done in Node.js is event-based. In most cases, the Emitter Object interacts with several observers to listen for messages.
Any event-driven module in Node.js, such as NET, extends a root class called EventEmitter.
EventEmitter in Node.js has two basic methods: on and EMIT.
Consider the following simple HTTP server:
const net = require("net"); Const server = net.createserver ().listen(8081, "127.0.0.1"); server.on("listening", function () { console.log("Server listening!" ); }); server.on("connection", function (socket) { console.log("Client connected!" ); socket.end("Hello client!" ); });Copy the code
Let’s listen to two events: Listening and Connection. In addition to these events, the Event Emitters also exposes an error event in case an error occurs.
If you run this code on port 80, instead of listening on the previous example, you get an exception:
const net = require("net"); Const server = net.createserver ().listen(80, "127.0.0.1"); server.on("listening", function () { console.log("Server listening!" ); }); server.on("connection", function (socket) { console.log("Client connected!" ); socket.end("Hello client!" ); });Copy the code
Output:
events.js:291 throw er; // Unhandled 'error' event ^ Error: listen EACCES: Permission denied 127.0.0.1:80 Emitted 'error' event on Server instance at:...Copy the code
To catch it, we can register an error event handler:
server.on("error", function(error) {
console.error(error.message);
});
Copy the code
Output result:
Listen EACCES: Permission denied 127.0.0.1:80Copy the code
conclusion
In this tutorial, we’ve covered JavaScript error handling, from simple synchronous code to advanced asynchrony. In JavaScript programs, you can catch exceptions in a number of ways.
Exceptions in synchronized code are the easiest to catch. In contrast, exceptions in asynchrony require some finesse to handle.
The new JavaScript apis in browsers almost all favor Promise. Then /catch/finally or try/catch modes are made easier for async/await exception handling.
communication
This article continues to update every week, you can wechat search “big move the world” the first time to read, reply [welfare] there are many front-end video waiting for you, this article GitHub github.com/qq449245884… Already included, welcome Star.