[Перевод] Улучшена обработка ошибок в async/await.

JavaScript

Оригинальная ссылка:Better error handling with async/await, Собио Дарлингтон

Эта статья знакомит с использованиемasync/awaitсинтаксис, лучший способ обработки ошибок. Перед этим вам также нужно понять, как работают промисы.

От ада обратных вызовов к обещаниям

Callback Hell, также известный как «Пирамида судьбы», представляет собой анти-шаблон, встречающийся в коде разработчика, который не является разумным способом асинхронного программирования.. - Колин То

Из-за вложенности функций обратного вызова,ад обратного звонкаЗаставляет ваш код течь вправо, а не вертикально вниз.

Для того, чтобы отразить функцию обратного вызова более интуитивно, вот пример.


Профиль пользователя, случай 1

// Code that reads from left to right 
// instead of top to bottom

let user;
let friendsOfUser;

getUser(userId, function(data) {
  user = data;

  getFriendsOfUser(userId, function(friends) {
    friendsOfUser = friends;

    getUsersPosts(userId, function(posts) {
      showUserProfilePage(user, friendsOfUser, posts, function() {
        // Do something here

      });
    });
  });
});

Promise

Промисы — это языковая функция, представленная в ES2015 (широко известная как ES6), чтобы лучше обрабатывать асинхронные операции и избегать ада обратных вызовов.

В следующем примере используется Promise.thenchain для решения проблемы ада обратного вызова.

Профиль пользователя, случай 2

// A solution with promises

let user;
let friendsOfUser;

getUser().then(data => {
  user = data;

  return getFriendsOfUser(userId);
}).then(friends => {
  friendsOfUser = friends;

  return getUsersPosts(userId);
}).then(posts => {
  showUserProfilePage(user, friendsOfUser, posts);
}).catch(e => console.log(e));

Обещания обрабатываются более чистым и читабельным способом.

async/await Promise

async/await— это специальный синтаксис для более лаконичной обработки промисов.

существуетfuntionдобавить передasyncКлючевое слово преобразует функцию в обещание.

Возвращаемое значение всех асинхронных функций — обещание.

пример

// Arithmetic addition function
async function add(a, b) {
  return a + b;
}

// Usage: 
add(1, 3).then(result => console.log(result));

// Prints: 4

использоватьasync/await, чтобы вариант профиля пользователя 2 выглядел лучше.

Профиль пользователя, случай 3

async function userProfile() {
  let user = await getUser();
  let friendsOfUser = await getFriendsOfUser(userId);
  let posts = await getUsersPosts(userId);

  showUserProfilePage(user, friendsOfUser, posts);
}

так далее! Существует проблема

В «Случае профиля пользователя 3», если обещание отклоняется, оно выдаетUnhandled promise rejectionаномальный.

Ни в одном коде, написанном до этого, не учитывался отказ от Promise. Необработанные промисы отказа раньше молча терпели неудачу, что могло превратить отладку в кошмар.

Но теперь Promise отклоняется с ошибкой.

  • Ошибка, выданная Google Chrome:VM664:1 Uncaught (in promise) Error
  • Ошибка, выдаваемая узлом, выглядит так:(node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (r ejection id: 1): Error: spawn cmd ENOENT

[1] (node:4796) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Ни одно промис не должно остаться незамеченным (каждое промис использует.catchобработка).- Javascript

Обратите внимание, что в «Случае профиля пользователя 2».catchметод. если не написано.catchblock, JavaScript выдаст, когда Promise отклонитUnhandled promise rejectionошибка.

Легче решить проблему в User Profile Case 3. просто используйтеtry...catchпод блок-упаковкойawaitпредложения, которых следует избегатьUnhandled promise rejectionнеправильно.

Профиль пользователя, случай 4

async function userProfile() {
  try {
    let user = await getUser();
    let friendsOfUser = await getFriendsOfUser(userId);
    let posts = await getUsersPosts(userId);

    showUserProfilePage(user, friendsOfUser, posts);
  } catch(e) {
    console.log(e);
  }
}

Проблема решена!

Но может ли обработка ошибок быть более элегантной?

Как узнать, из какого асинхронного запроса возникла ошибка?

Может использоваться в асинхронных запросах.catchспособ обработки ошибок.

Профиль пользователя, случай 5

let user = await getUser().catch(e => console.log('Error: ', e.message));

let friendsOfUser = await getFriendsOfUser(userId).catch(e => console.log('Error: ', e.message));

let posts = await getUsersPosts(userId).catch(e => console.log('Error: ', e.message));

showUserProfilePage(user, friendsOfUser, posts);

Приведенное выше решение будет обрабатывать одну ошибку из запроса, но будет смешивать шаблоны. Должен быть более чистый способ использованияasync/awaitбез использования.catchметод (ну, вы можете сделать это, если вы не возражаете).

Мое лучшее решение для обработки ошибок async/await

Случай с профилем пользователя 6

/**
 * @description ### Returns Go / Lua like responses(data, err) 
 * when used with await
 *
 * - Example response [ data, undefined ]
 * - Example response [ undefined, Error ]
 *
 *
 * When used with Promise.all([req1, req2, req3])
 * - Example response [ [data1, data2, data3], undefined ]
 * - Example response [ undefined, Error ]
 *
 *
 * When used with Promise.race([req1, req2, req3])
 * - Example response [ data, undefined ]
 * - Example response [ undefined, Error ]
 *
 * @param {Promise} promise
 * @returns {Promise} [ data, undefined ]
 * @returns {Promise} [ undefined, Error ]
 */
const handle = (promise) => {
  return promise
    .then(data => ([data, undefined]))
    .catch(error => Promise.resolve([undefined, error]));
}

async function userProfile() {
  let [user, userErr] = await handle(getUser());

  if(userErr) throw new Error('Could not fetch user details');

  let [friendsOfUser, friendErr] = await handle(
    getFriendsOfUser(userId)
  );

  if(friendErr) throw new Error('Could not fetch user\'s friends');

  let [posts, postErr] = await handle(getUsersPosts(userId));

  if(postErr) throw new Error('Could not fetch user\'s posts');

  showUserProfilePage(user, friendsOfUser, posts);
}

Здесь используется функция полезностиhandle, чтобы избежатьUnhandled promise rejectionОтчеты об ошибках и детальная обработка ошибок.

объяснять

handleФункция принимает объект Promise в качестве параметра и всегда разрешает его, заканчивая[data|undefined, Error|undefined]возвращает результат в виде .

  • Если Обещание разрешается,handleвозврат функции[data, undefined];
  • Если Обещание отвергается,handleвозврат функции[undefined, Error].

похожие решения

В заключение

async/awaitСинтаксис удобен, но вам все равно придется иметь дело с выброшенными ошибками в асинхронных функциях.

Трудно справиться, если вы не реализуете пользовательские классы ошибокPromise.thenв цепи.catchОбработка ошибок.

использоватьhandleфункции полезности, мы можем избежатьUnhandled promise rejectionОтчеты об ошибках и детальная обработка ошибок.

(Конец текста)


коммерческое время(длительный эффект)

Моя хорошая знакомая владеет питомником, и я здесь, чтобы помочь ей распространить информацию. Теперь все кошки в питомникекукла кошка. Если вы также любите кошек и нуждаетесь в этом, вы можете также отсканировать ее [Xianyu] QR-код. Неважно, если вы не купите его, просто взгляните.

(над)