The most complete guide to JavaScript error handling (part 2)

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 GitHubhas 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 Promiseapproach, 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.rejectand 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 thencontent to receive a return, or with catchwrong 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 thenand catch, Promise there finallya method, which is similar try/catchin 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 fetchis 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));

catchIntercept 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 catchhandle 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.allthe result is to run the function, it finallywill 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 promiseand AggregateErrorinstance of the type, which is Errora 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 catchhandle errors, and the output is as follows:

AggregateError: No Promise in Promise.any was resolved
Always runs!

AggregateErrorThe object has basically the Errorsame attributes, plus errorsattributes:

//
  .catch(error => console.error(error.errors))
//

This attribute is rejectan 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 rejectit 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 fulfilledor rejectedafter 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.allSettledan array of two Promises: one is resolved and the other is rejected.

This situation catchwill not be executed, finallywill 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 toUppercaseand convert it to an asynchronous function by placing it functionbefore 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 asyncof the function and the function will return one Promise. This means we can be carried out after the function call then, catchand finallyoperations

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 asyncexception is thrown function, we can use catchto 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 generatorsis 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 throwwill trigger the Promise reject, we can use catchthem 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 yieldlater, 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/awaitthat we can use try/catchto 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/finallycan 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.readFileany 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/catchcapturing 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
  });
}

errorHandlerAs 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 EventEmitterroot class called .

EventEmitter in Node.js has two basic methods: onand 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: listeningand connection. In addition to these events, event emitters also a public errorevent, 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 errorevent 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/finallyOr try/catchthe mode async/awaitof 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 .

Guess you like

Origin blog.csdn.net/qq449245884/article/details/108613977