1. try/catch

Try /catch is basically the one you use most often with async/await, and basically we use it to surround most asynchronous methods. Once the promise after the await keyword is rejected, an exception error is thrown.

run();
async function run() {
    try {
        await Promise.ject(new Error('Oops! '));
    } catch (err) {
        console.error(error.message); }}Copy the code

Try /catch can also handle synchronous errors, such as the following:

async function run() {
  const v = null;
  try {
    await Promise.resolve('foo');
    v.thisWillThrow;
  } catch (error) {
        // "TypeError: Cannot read property 'thisWillThrow' of null"
      console.error(error.message); }}Copy the code

returnThe problem

Does it seem like we can just mindlessly put all our logic into try/catch and be fine? Not exactly, but the following code causes unhandled Promise Rejection. The return keyword returns an error without being caught:

async function run() {
    try {
        Return the Promise directly instead of using the await keyword
        return Promise.reject(new Error('Oops! '));
    } catch (error) {
        console.error(error.message); }}Copy the code

The solution here is to use return await:

async function run() {
    try {
        return await Promise.reject(new Error('Oops! ')); } catch (error) { console.error(error.message); }}Copy the code

The callback problem

Another problem is that a try catch does not catch callback functions. Try catch only works in a single execution environment. Here we add a try catch to the callback to catch errors:

setTimeout(funciton() {
  try {
    fn()
  } catch (e) {
      // handle error}})Copy the code

It works, but try catch happens everywhere. The V8 engine discouragestry catches in functions. Previous attempts to move a try catch to the top level to catch errors in the call stack did not work for asynchronous code.

2. Golang-style(then)

Golang style uses the. Then () method to convert a promise into a reject promise that processes the error. You can use something like if(err) to check:

async function throwAnError() {
    throw new Error('Opps! ');
}

async function runAwait() {
    let err = await throwAnError();
    if (err){
       console.error(err.message); }}Copy the code

This will throw an exception directly, because the method throws an exception, but the method itself is not caught by a try/catch. A lot of times, this happens when we use third-party libraries.

Then () solution

async function runAwait() {
       let err = await throwAnError().then((a)= > null, err => err);
    if (err){
       console.error(err.message); }}Copy the code

The then() method waits for the promise state resolve or Reject to execute the corresponding callback, then determines the err object and processes it, so it’s essentially caught.

Return both an error and a value

async function run() {
    let [err, res] = await throwAnError().then(v= > [null, v], err => [err, null]);
    if (err){
        console.error(err.message);
    }
    console.log(res)
}
Copy the code

Results:

Doing so returns an array of results and error objects by deconstruction. Reject, of course, returns null and error; If the error object that Resolved returns is null, the second will be the result.

The advantages and disadvantages

  • Advantages: This mode can be handled more succinctly, while eliminating the need to writecatch.
  • Disadvantage 1: This is very repetitive and requires judgment every time an asynchronous operation is performederrorObject.
  • Disadvantage 2: Can’t help with processingrunSynchronization error in method. So this approach needs to be used with caution.

3. Catch caught

Both modes can handle asynchronous errors, but for error handling, it is best to include a catch at the end of the asynchronous logic to ensure that all errors are caught. This is also a principle of treating errors collectively rather than individually.

async function run() {
  return Promise.reject(new Error('Oops! '));
}

run().catch(function handleError(err) {
    console.error(err.message);
}).catch( err= > {
    process.nextTick((a)= > { throw errl});
})

Copy the code

Catch an error using a catch. If handleError has an error of its own, it needs to catch again, but to avoid callback hell, it terminates the process if the method has an error.

The advantages and disadvantages

  • usecatchThe asynchronous method catches asynchronous errors regardless of whether or not it catches errors.
  • usetry/catchUnable to avoidcatchItself throws an exception, and if it throws that in addition to nesting one more layertry/catchThe best thing to do is to addcatchTo make the code cleaner.

4 Global error capture

4.1 Browser global error capture

Browser global processing is basically event-driven because browsers are event-driven. Once an error is thrown, the interpreter stops execution and expands in the execution context, at which point an onError global event is thrown:

window.addEventListener('error'.function (e) {
    var error = e.error;
    console.log(error);
})
Copy the code

The global error handler catches any errors that occur in the execution environment, even error events for different objects, or various types of errors. This is a common way to handle errors globally.

The call stack

The call stack is very important in locating problems and can be used to handle specific errors within the processor.

window.addEventListener('error'.function (e) {
  var stack = e.error.stack;
  var message = e.error.toString();
  if (stack) {
    message += '\n' + stack;
  }
  var xhr = new XMLHttpRequest();
  xhr.open('POST'.'/log'.true);
  // Fire an Ajax request with error details
  xhr.send(message);
});
Copy the code

The log allows you to see exactly what conditions triggered what errors. The call stack can also be useful when debugging. You can analyze the log and see what conditions triggered the error.

Note:

Cross-domain scripts do not see errors. In JS, error messages are only allowed in the same field.

Personal ideas

More often than not, the code throws an exception, and we care more about what the value of a variable is at run time and whether the value of the variable caused the error, so it’s more important to print out more information about the call.

4.2 Node.js global error capture

Node.js’s own exception handling is much more complicated because it involves processes or threads throwing exceptions.

Koa-based global error handling

Nodejs is an error-first asynchronous processing mechanism where the underlying net module’s LISTEN method is called and a callback is executed when an error occurs.

app.listen(app.config.listenPort, (err) => {
  if (err) throw err
  app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`)})Copy the code

Route error Handling

It may also have different error handling logic for each route, so incoming requests may need to return different exception codes and information depending on the situation.

router.get('/loginAuth'.async (ctx, next) => {
  try {
    const code = query.code
    const res = await requestToken(code)
    if(res.data.code ! = =0) {
      ctx.app.logger.error(`request token error.Code is ${res.data.code} || response is: The ${JSON.stringify(res.data.data)} || msg: ${res.data.message}`)
      ctx.body = {
        code: 10000.message: `request token by code error`}}else {
      ctx.body = res.data
    }
  } catch (err) {
    ctx.app.logger.error(`request api has exception ${ctx.request.url} || ${err.code} ||  ${err.message} || ${err.stack}`)
    ctx.body = {
      code: 500.message: `Error response`}}})Copy the code

5. To summarize

  • Usually the exception may be expected or exceed the expected, however, usingtry/catchNo problem.
  • Use as much as possible for unexpected errorscatchTo make sure they’re captured.
  • Add the error handler towindowObject, it will catch an asynchronous errorDRYandSOLIDThe principle. A global error handler can help you keep asynchronous code clean.

Reference

async-await-error-handling

nodejs-v12-lts