[JavaScript] async await для более элегантной обработки ошибок

внешний интерфейс JavaScript спецификация кода
[JavaScript] async await для более элегантной обработки ошибок

задний план

У команды появился новый партнер, и мы обнаружили, что в спецификации кода нашей команды нам нужно датьasync awaitДобавить кtry...catch. Он был очень растерян: если их много (не сосредоточено), не нужно ли добавить много мест? Разве это не неэлегантно?

Почему обработка ошибок

JavaScriptЭто однопоточный язык, если не добавленtry ...catch, это вызовет прямую ошибку и не сможет продолжить выполнение. Конечно, это не означает, что вы должны использовать его в своем коде.try...catchзавернуть, использоватьtry...catchозначает, что вы знаете, что этот код местоположения может сообщить об ошибке, поэтому вы используетеtry...catchЗахватите обработку и дайте программе продолжить работу.

Я понимаю, что мы обычно делаемasync awaitКогда он работает в асинхронном сценарии, этот сценарий не должен блокировать процесс, поэтому рекомендуется использоватьtry...catchобработка.

async await для более элегантной обработки ошибок

Но, как сказал коллега,try...catchНе очень элегантное поведение. Так что яGoogleЧерез некоторое время я нашелHow to write async await without try-catch blocks in JavascriptБолее элегантный подход упоминается в этом посте и упакован в библиотеку -await-to-js. В этой библиотеке только одинfunction, мы можем полностью применить эту функцию к нашему бизнесу следующим образом:

/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function to<T, U = Error> (
  promise: Promise<T>,
  errorExt?: object
): Promise<[U, undefined] | [null, T]> {
  return promise
    .then<[null, T]>((data: T) => [null, data]) // 执行成功,返回数组第一项为 null。第二个是结果。
    .catch<[U, undefined]>((err: U) => {
      if (errorExt) {
        Object.assign(err, errorExt);
      }

      return [err, undefined]; // 执行失败,返回数组第一项为错误信息,第二项为 undefined
    });
}

export default to;

Здесь должен быть пункт предварительных знаний:await ожидает возвращаемого значения обещания.

При нормальных обстоятельствах,awaitЗа командой следуетPromiseобъект, возвращает результат этого объекта. если неPromiseобъект, он напрямую возвращает соответствующее значение.

Так что нам просто нужно использоватьPromiseхарактеристики соответственноpromise.thenа такжеpromise.catchвозвращает отдельный массив, в которомfulfilledКогда первый элемент возвращаемого массиваnull, второе - результат.rejected, первый элемент возвращаемого массива — это сообщение об ошибке, а второй —undefined. При использовании оцените, пуст ли первый элемент, вы можете узнать, есть ли ошибка, конкретное использование заключается в следующем:

import to from 'await-to-js';
// If you use CommonJS (i.e NodeJS environment), it should be:
// const to = require('await-to-js').default;

async function asyncTaskWithCb(cb) {
     let err, user, savedTask, notification;

     [ err, user ] = await to(UserModel.findById(1));
     if(!user) return cb('No user found');

     [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) return cb('Error occurred while saving task');

    if(user.notificationsEnabled) {
       [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if(err) return cb('Error while sending notification');
    }

    if(savedTask.assignedUser.id !== user.id) {
       [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
       if(err) return cb('Error while sending notification');
    }

    cb(null, savedTask);
}

резюме

async awaitДобавление обработки ошибок в я лично считаю необходимым, но решение не толькоtry...catch. использоватьasync awaitа такжеPromiseС помощью этой функции мы можем более изящно обрабатывать ошибки асинхронного ожидания.