Как изящно обрабатывать исключения Async/Await?

Node.js JavaScript

Переводчик пресса:использовать.catch()поймать все исключения

В этой статье используется свободный перевод, и авторские права принадлежат оригинальному автору.

Обработка исключений в async/await сбивает с толку. Хотя есть много способов справиться сИсключения для асинхронных функций, но даже опытные разработчики иногда ошибаются.

Предположим, у вас есть файл с именемrun()асинхронная функция. В этой статье я опишу 3 способа борьбы сrun()исключение:try/catch, Языковой стиль Go, используется при вызове функцииcatch()(которыйrun().catch()). Я объясню вам, почему это на самом деле все, что вам нужноcatch()достаточно.

try/catch

когда вы впервые используетеasync/await, вы можете попробовать использоватьtry/catchОкружите каждую асинхронную операцию. если выawaitОтклоненный Promise, JavaScript выдает уловимую ошибку.

run();

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

try/catchСпособен перехватывать неасинхронные исключения.

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;
    }
}

Итак, просто используйте всю логику кодаtry/catchМожно ли это сделать, окружив его? Не совсем. Код ниже выдастunhandled promise rejection. awaitпреобразует отклоненное обещание в перехватываемую ошибку, ноreturnнет.

run();

async function run() {
    try {
        // 注意这里是return,不是await
        return Promise.reject(new Error("Oops!"));
    } catch (error) {
        // 代码不会执行到这里
    }
}

также невозможно использоватьreturn awaitобойти.

Еще одним недостатком является то, что использованиеtry/catchПосле этого трудно использовать.синтаксис для цепочки промисов.

Использование синтаксиса Go

Другим распространенным способом является использованиеthen()пришлось бы использоватьcatch()для преобразования захваченного и обработанного промиса в обычный промис. Затем, как и в Go, используйтеif(err)для обработки исключений.

run();

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

async function noError() {
    return 42;
}

async function run() {
    // The `.then(() => null, err => err)` 来匹配正常/异常的情况。如果正常情况,返回`null`;如果异常,返回`err`
    let err = await throwAnError().then(() => null, err => err);
    if (err != null) {
        err.message; // 'Oops'
    }

    err = await noError().then(() => null, err => err);
    err; // null
}

Если вы действительно хотите вернуть и ошибку, и правильное значение, вы можете полностью притвориться, что находитесь в 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
    // 你可以使用数组解构来匹配err和返回值
    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
}

Использование обработки ошибок в стиле Go не избавляет от этого.returnНеуловимая ситуация. И это усложняет весь код, если вы забудетеif(err != null), будет проблема.

В общем, есть два существенных недостатка:

  1. Код чрезвычайно повторяющийся, и каждое место необходимоif (err != null), действительно утомительно, и его легко пропустить;
  2. run()Неасинхронные ошибки в функциях также не могут быть обработаны;

В целом ничем не лучшеtry/catchНасколько лучше.

используется при вызове функцииcatch()

try/catchИ пойти на языковые аномалии в стиле языка имеют свои собственные сценарии использования, но лучший способ обрабатывать все исключенияrun()последующее использование функцииcatch(),нравится:run().catch(). Другими словами, используйтеcatch()иметь дело сrunВсе ошибки в функции, не дляrunВ каждом случае напишите код для соответствующей обработки.

run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    // 在handleError中处理所有的异常
    // 如果handleError出错,则退出。
    .catch(err => {
        process.nextTick(() => {
            throw err;
        });
    });

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

Помните,асинхронные функции всегда возвращают промисы. Обещание будет отклонено всякий раз, когда в функции есть исключение. Кроме того, если асинхронная функция возвращает промис, который отклоняется, промис будет продолжать отклоняться.

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

async function run() {
    // 注意:这里使用了return,而不是await
    return Promise.reject(new Error("Oops!"));
}

зачем использоватьrun().catch()а не весьrun()функцияtry/catchА как насчет завернуть? Сначала рассмотрим ситуацию: еслиtry/catchизcatchЕсть исключения, что делать? Есть только один путь: вcatchзатем используйте внутрьtry/catch. так,run().catch()Шаблон делает обработку исключений очень лаконичной.

Суммировать

Нам лучше иметь глобальный обработчик ошибок для обработки исключений, которые не учитываются, например, использованиеrun().catch(handleError), вместоrun()Добавьте все возможные ошибки в функциюtry/catch.

О Фундебаге

FundebugСосредоточьтесь на JavaScript, апплете WeChat, мини-игре WeChat, апплете Alipay, React Native, Node.js и мониторинге ошибок онлайн-приложений Java в режиме реального времени. С момента официального запуска Double Eleven в 2016 году Fundebug обработала в общей сложности более 1 миллиарда ошибок, включая Sunshine Insurance, Walnut Programming, Lizhi FM, Head 1:1, Weimai, Youth League Club и многие другие бренды. Бесплатная пробная версия приветствуется!

img

Уведомление об авторских правах

Пожалуйста, указывайте автора при перепечаткеFundebugИ адрес этой статьи:ошибка blog.fun.com/2019/07/24/…