Каков правильный шаблон для всплывающих и перехвата исключений с помощью async / await? - PullRequest
0 голосов
/ 03 февраля 2019

Я изо всех сил пытаюсь сосредоточиться на том, что является правильным шаблоном для обработки ошибок во вложенных подпрограммах await / async, сохраняя при этом код чистым и простым.(несмотря на чтение бесчисленных статей и блогов)

У меня есть набор функций, которые (принципиально) похожи на следующие:

async validate(params) {
    const recCount = await this._getCount(db, params);

    if( recCount > 0 )
        return "Record already exists";
}

_getCount - это оболочка, которая создает sql

async _getCount(conn, regdata) {
    const sql = "SELECT count(*) AS 'count' FROM myTable WHERE product = ? and category = ?";
    let rows = await this._execSQL(conn, sql, [ regdata.product, regdata.category ]);
    return rows[0].count;
}

и фактический запрос выполняется следующим образом:

async _execSQL(conn, sql, data) {
    const [ rows ] = await conn.query(sql, data);
    return rows;
}

Метод conn.query (из библиотеки mysql2 / promise ) будетотклонить обещание, если запрос не удался.

Итак, мой вопрос состоит в том, каков правильный шаблон для обработки исключений?

В синхронном мире я ничего не мог сделать ни _execSQL , ни _getCount , а просто перехватить исключение в validate ;просто естественно допустить возникновение исключения.

Однако в асинхронном мире, как я могу сделать эквивалент, не получая исключение «Необработанное обещание»?

Неужели я застрял с необходимостью отлавливать ошибку при каждой отдельной асинхронной процедуре на всех уровнях?

Или есть ли лучший способ без использования чего-то вроде process.on('unhandledRejection',...), когда кажется, что я обхожу проблему?

EDIT: добавлен пример и трассировка стека

Хорошо, я фактически добавил этот код в свое приложение и поместил try / catch в функцию validate.Дословный код:

async validate(db, params) {
    let recCount;

    try {
        recCount = await this._getCount(db, params);
    } catch (err) {
        console.log('Caught error', err);
    }

    if (recCount > 0) return 'Record already exists';
}

async _getCount(conn, regdata) {
    const sql = "SELECT count(*) AS 'count' FROM myTable WHERE product = ? and category = ?";
    let rows = await this._execSQL(conn, sql, [ regdata.product, regdata.category ]);
    return rows[0].count;
}

async _execSQL(conn, sql, data) {
    const [ rows ] = await conn.query(sql, data);
    return rows;
}

У меня есть обработчик события для unhandledRejection, который сообщает о событии вместе с внутренним исключением и трассировкой стека.Вот что он выбрасывает:

Stack Trace:

AppError: Unhandled promise rejection.   Plugin may not be properly handling error.
    at process.on (D:\Development\website\service\server.js:73:5)
    at emitTwo (events.js:126:13)
    at process.emit (events.js:214:7)
    at emitPendingUnhandledRejections (internal/process/promises.js:108:22)
    at process._tickCallback (internal/process/next_tick.js:189:7)

Inner Error:

{   "message": "connect ECONNREFUSED 127.0.0.1:3306",   "code": "ECONNREFUSED",   "errno": "ECONNREFUSED" }

Error: connect ECONNREFUSED 127.0.0.1:3306
    at PromisePool.query (D:\Development\website\webhooks\node_modules\mysql2\promise.js:323:22)
    at Registration._execSQL (D:\Development\website\webhooks\plugins\registration.js:108:31)
    at Registration._logRequest (D:\Development\website\webhooks\plugins\registration.js:179:14)
    at Registration.register (D:\Development\website\webhooks\plugins\registration.js:52:8)
    at Router.exec (D:\Development\website\service\router.js:119:20)
    at IncomingMessage.request.on (D:\Development\website\service\server.js:292:47)
    at emitNone (events.js:106:13)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)

1 Ответ

0 голосов
/ 03 февраля 2019

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

async function f1() { return await f2(); }
async function f2() { return await f3(); }
async function f3() {
  return Promise.reject('no way!');
  // or
  throw 'no way!';
}

async function f_await() {
  try {
    console.log('never succeeds here', await f1());
  } catch (err) {
    console.log('I check for errors at the level I prefer!');
    throw 'I can even intercept and rethrow!';
  }
  return 'or i can keep going like eveything is fine';
}
function f_then() {
  f1().then(console.log.bind(null, 'never succeeds here'))
  .catch(function (err) {
    console.log('I check for errors at the level I prefer!');
    throw 'I can even intercept and rethrow!';
  }).then(function () {
    return 'or i can keep going like eveything is fine';
  });
}

Если вы запускаете необработанное предупреждение об отказе, это потому, что ... вы не обрабатывали какое-либо отклонение в любомточка в цепочке, в то время как вам всегда нужно: даже в синхронном коде, если исключение вызывается и никогда не перехватывается, компьютер скажет вам, насколько он несчастен.

Если вы считаете, что лучший способ справиться с этимSQL-запрос, отклоняемый в вашем коде, находится в validate, затем сделайте это: окружите этот await блоком try / catch и «обработайте» ошибку в catch так, как вы думаетелучше всего ... Не уверен, что вижу здесь проблему!

...