Translators press: use.catch() to catch all exceptions

  • 原文: Async Await Error Handling in JavaScript
  • Translator: Fundebug

The copyright of this article belongs to the original author

Exception handling in async/await is confusing. While there are many ways to handle async exceptions, even experienced developers sometimes get it wrong.

Suppose you have an asynchronous function called run(). In this article, I describe three ways to handle run() exception cases: try/catch, Go language style, and function calls using catch()(that is, run().catch()). I’ll explain to you why it’s almost enough to just catch().

try/catch

The first time you use async/await, you might try to surround each async operation with a try/catch. If you await a reject Promise, JavaScript throws an error that can be caught.

run();

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

Try /catch can catch non-asynchronous exceptions.

run();

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

So, you can just wrap all your code logic around a try/catch, right? Not exactly. The following code throws an unhandled Promise rejection. Await to convert a rejected promise into a grabable error, but the return does not.

run();

async function run() {
    try {
        // Note that this is return, not await
        return Promise.reject(new Error("Oops!"));
    } catch (error) {
        // The code will not be executed here}}Copy the code

It is also impossible to get around with a return await.

Another disadvantage is that once you use try/catch, it’s hard to use. “To make Promise chain combinations.

Use the Go syntax

Another common way to do this is to use then() to turn a Promise that would otherwise need to be caught and processed with a catch() into a regular Promise. You then use if(ERR) to handle exceptions as in Go.

run();

async function throwAnError() {
    throw new Error("Oops!");
}

async function noError() {
    return 42;
}

async function run() {
    // The '. Then (() => NULL, err => err) 'to match normal/abnormal conditions. If normal, return 'null'; If an exception occurs, 'err' is returned
    let err = await throwAnError().then((a)= > null, err => err);
    if(err ! =null) {
        err.message; // 'Oops'
    }

    err = await noError().then((a)= > null, err => err);
    err; // null
}
Copy the code

If you really want to return error and correct values at the same time, you can completely pretend to be using Go.

run();

async function throwAnError() {
    throw new Error("Oops!");
}

async function noError() {
    return 42;
}

async function run() {
    // The `.then(v => [null, v], err => [err, null])` pattern
    // You can use array deconstruction to match err and return values
    let [err, res] = await throwAnError().then(
        v= > [null, v],
        err => [err, null]);if(err ! =null) {
        err.message; // 'Oops'
    }

    err = await noError().then(v= > [null, v], err => [err, null]);
    err; // null
    res; / / 42
}
Copy the code

Using Go style error handling does not get rid of situations that return cannot catch. It also makes the whole code more complicated if you forget if(err! = null), there is a problem.

In general, there are two major disadvantages:

  1. The code is extremely repetitive, everywhereif (err ! = null), really tired, and easy to miss;
  2. run()Non-asynchronous errors in functions cannot be handled either;

Overall, it’s not much better than try/catch.

Used during function callscatch()

Both try/catch and Go language style exception handling have their own usage scenarios, but the best way to handle all exceptions is to use catch() after the run() function, like this :run().catch(). In other words, use a catch() to handle all errors in the run function, rather than writing code to handle each case in the run.

run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    // Handle all exceptions in handleError
    // Exit if handleError fails.
    .catch(err= > {
        process.nextTick((a)= > {
            throw err;
        });
    });

async function run() {
    await Promise.reject(new Error("Oops!"));
}
Copy the code

Remember that async functions always return promises. As long as there is an exception in the function, Promise will reject. Also, if an async function returns a reject Promise, the Promise will continue to be rejected.

run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    .catch(err= > {
        process.nextTick((a)= > {
            throw err;
        });
    });

async function run() {
    // Note: return is used instead of await
    return Promise.reject(new Error("Oops!"));
}
Copy the code

Why use run().catch() instead of wrapping the entire run() function around a try/catch? Let’s first consider the case: if the catch part of a try/catch has an exception, what should we do? There is only one way to do this: use try/catch inside a catch. So the run().catch() mode makes exception handling very neat.

conclusion

It is better to have a global errorHandler to handle unexpected exceptions, such as run().catch(handleError), than to add try/catch to all possible errors in run().

About Fundebug

Fundebug focuses on real-time BUG monitoring for JavaScript, wechat applets, wechat games, Alipay applets, React Native, Node.js and Java online applications. Since its official launch on November 11, 2016, Fundebug has handled over 1 billion error events in total, and paid customers include Sunshine Insurance, Walnut Programming, Lychee FM, Zhangmen 1-to-1, Weimai, Qingtuanshe and many other brand enterprises. Welcome to try it for free!

Copyright statement

Reprint please indicate the author Fundebug and this article addresses: blog.fundebug.com/2019/07/24/…