Author: Valentino Gagliardi
Translator: Front Ash
Source: valentinog
Like it and look again , WeChat search [Daqian World] focuses on this person who has no background in a big factory, but has a positive attitude towards upwards. This article
GitHub
has been included on https://github.com/qq449245884/xiaozhi, the article has been classified, and a lot of my documents and tutorial materials have been sorted out.
Everyone said that there was no project to write on the resume, so I helped you find a project and also included a [Building Tutorial] .
Use Promise to handle errors
To illustrate the Promise
approach, let's go back to the beginning of the case:
function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();
}
toUppercase(4);
Relatively simple throws 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);
}
Because of the use of the Promise , you can use then
content to receive a return, or with catch
wrong to capture appears.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message));
The above execution result:
Wrong type given, expected a string
In addition to then
and catch
, Promise there finally
a method, which is similar try/catch
in finally
.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message))
.finally(() => console.log("Run baby, run"));
Promise, error, 和 throw
Using Promise.reject can easily throw errors:
Promise.reject(TypeError("Wrong type given, expected a string"));
Besides Promise.reject
, we can also exit Promise by throwing an exception.
Consider the following example:
Promise.resolve("A string").then(value => {
if (typeof value === "string") {
throw TypeError("Expected a number!");
}
});
To stop the abnormal propagation, we use as usual catch
:
Promise.resolve("A string")
.then(value => {
if (typeof value === "string") {
throw TypeError("Expected a number!");
}
})
.catch(reason => console.log(reason.message));
This pattern fetch
is very common in:
fetch("https://example-dev/api/")
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(json => console.log(json));
catch
Intercept exceptions can be used here . If we fail, or decide not to catch it, exceptions can bubble freely in the stack.
Use Promise to handle exceptions in timers
Using timers or events cannot catch exceptions raised from callbacks.
function failAfterOneSecond() {
setTimeout(() => {
throw Error("Something went wrong!");
}, 1000);
}
// DOES NOT WORK
try {
failAfterOneSecond();
} catch (error) {
console.error(error.message);
}
The solution is to use Promise:
function failAfterOneSecond() {
return new Promise((_, reject) => {
setTimeout(() => {
reject(Error("Something went wrong!"));
}, 1000);
});
}
To use reject
, we initiate a Promise rejection, which carries an error object.
At this point, we can use to catch
handle exceptions:
failAfterOneSecond().catch(reason => console.error(reason.message));
Use Promise.all to handle errors
Promise.all(iterable)
The method returns a Promise instance, this instance is "resolved" when all the promises in the iterable parameter are "resolved" or the parameter does not contain a promise, the callback is completed (resolve);
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!' ]
If one of the promises in the parameter fails (rejected), the callback of this instance fails (reject), and the reason for the failure 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!
Similarly, no matter how Promise.all
the result is to run the function, it finally
will be executed:
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(error => console.error(error.message))
.finally(() => console.log("Always runs!"));
Use Promise.any to handle errors
Promise.any()
(Firefox> 79, Chrome> 85) Receive a Promise iterable object, as long as one of the promises succeeds, it returns the promise that has succeeded. If the iteration object can not promise a success (i.e., all promises failed / rejected), it returns a failure promise
and AggregateError
instance of the type, which is Error
a sub-class for the single error together. In essence, this method Promise.all()
is the opposite.
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!"));
Here, we use to catch
handle errors, and the output is as follows:
AggregateError: No Promise in Promise.any was resolved
Always runs!
AggregateError
The object has basically the Error
same attributes, plus errors
attributes:
//
.catch(error => console.error(error.errors))
//
This attribute is reject
an array of each individual error generated by
[Error: "No good, sorry!, Error: "Bad day ..."]
Use Promise.race to handle errors
Promise.race(iterable)
The method returns a promise. Once a promise in the iterator is resolved or rejected, the returned promise will be 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!
This shows that the first Promise is executed more than the second line. What about the situation that includes 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!
What if you put reject
it 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!
Use Promise.allSettled to handle errors
Promise.allSettled()
The method returns a promise after fulfilled
or rejected
after all the given promises, with an array of objects, each of which represents 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!"));
We pass it to Promise.allSettled
an array of two Promises: one is resolved and the other is rejected.
This situation catch
will not be executed, finally
will always be executed.
[
{ status: 'fulfilled', value: 'Good!' },
{
status: 'rejected',
reason: Error: No good, sorry!
}
]
Use async/await to handle errors
For the sake of simplicity, we use the previous synchronous function toUppercase
and convert it to an asynchronous function by placing it function
before the keywordasync
async function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();
}
Just add it in front async
of the function and the function will return one Promise
. This means we can be carried out after the function call then
, catch
and finally
operations
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!"));
When the async
exception is thrown function, we can use catch
to capture.
The most important thing is that in addition to this way, we can also use it try/catch/finally
, just like we do 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();
Output:
Wrong type given, expected a string
Always runs!
Use async generators to handle errors
JavaScript async generators
is a generator function that can generate Promises instead of simple values.
async function* asyncGenerator() {
yield 33;
yield 99;
throw Error("Something went wrong!"); // Promise.reject
}
Based on Promise, the same rules for error handling apply here. In asynchronous Builder throw
will trigger the Promise reject
, we can use catch
them to intercept.
In order to use the Promise of the asynchronous generator, we can do this:
- then method
- Asynchronous traversal
From the above we know that in two calls yield
later, the next will throw an exception:
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));
Output result:
{ value: 33, done: false }
{ value: 99, done: false }
Something went wrong!
Another one is to use 异步遍历
the 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();
With async/await
that 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();
Output result:
33
99
Something went wrong!
The iterator object returned from the asynchronous generator function also has throw()
methods, very similar to its synchronous copy. Calling on the iterator object here throw()
will not raise an exception, but will be 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 undefined
To handle this situation from the outside, we can do:
go.throw(Error("Let's reject!")).catch(reason => console.error(reason.message));
Error handling in Node
Synchronous error handling in Node
The synchronization error handling in Node.js is not much different from what you have seen so far. For synchronization, use try/catch/finally
can be very good work.
Asynchronous error handling in Node.js: callback mode
For asynchronous code, Node.js mainly uses these two methods:
- Callback mode
- event emitters
In the callback mode, the asynchronous Node.js API accepts a function, which is processed through the 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
});
}
We can see that the way to handle errors here is to use callbacks:
//
function(error, data) {
if (error) console.error(error);
// do stuff with the data
}
//
If fs.readFile
any error is caused by reading the given path, we will get an error object.
At this point, we can:
- Simply type out the wrong object
- Throw 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
});
}
However, like events and timers in the DOM, this exception will crash the program. By try/catch
capturing it 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);
}
If we don't want to crash the program, 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
});
}
errorHandler
As the name suggests, here is 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.
}
Asynchronous error handling in Node.js: event emitters
Most of the work done in Node.js is based on events . In most cases, the emitter object interacts with some observers to listen for messages.
Any event-driven module in Node.js (such as net) extends a EventEmitter
root class called .
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!");
});
Here we listen to two events: listening
and connection
. In addition to these events, event emitters also a public error
event, in case an error occurs.
If you run this code on port 80 instead of listening on the previous example, you will 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!");
});
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: ...
To capture it, we can register an error
event handler:
server.on("error", function(error) {
console.error(error.message);
});
Output result:
listen EACCES: permission denied 127.0.0.1:80
to sum up
In this guide, we introduced various error handling in JavaScript, from simple synchronous code to advanced asynchronous. In JavaScript programs, there are many ways to catch exceptions.
Exceptions in synchronous code are the easiest to catch. On the contrary, exceptions in asynchronous require some skills to handle.
The new JavaScript API in the browser is almost always biased Promise
. then/catch/finally
Or try/catch
the mode async/await
of exception handling becomes easier.
The possible bugs after code deployment cannot be known in real time. In order to solve these bugs afterwards, a lot of time was spent on log debugging. By the way, I would like to recommend a useful BUG monitoring tool Fundebug .
Original: https://www.valentinog.com/blog/error/
The article is continuously updated every week, you can search on WeChat [Da Qian World] to read it as soon as possible, and reply [Welfare] There are many front-end videos waiting for you. This article has been included on GitHub https://github.com/qq449245884/xiaozhi , welcome to Star .