Цель этого руководства — показать вам, как вы можете использовать функцию async/await в новейшей версии Javascript. Я быстро и безболезненно объясню, как можно заменить промисы на async/await, правильно обработаю ошибки и рассмотрю простые синхронные и параллельные методы асинхронного ожидания.

Обязательно используйте последнюю версию nodejs (версия ›7.6.0), поскольку она поддерживает асинхронность/ожидание из коробки, но если вы не можете использовать последнюю версию node, вы можете использовать BabelJS для преобразования вашего кода в эквивалент ES6.

Для начала мы будем использовать две функции на основе промисов в качестве примеров асинхронных вызовов:

function getPentaCodeAvatar() {
  return new Promise((resolve, reject) => {
    github.search.users({ q: 'pentacode' }, (err, res) => {
      if (err) {
        reject(err);
        return;
      }
      let avatarUrl = '';
      if (res.data && res.data.items) {
        avatarUrl = res.data.items[0].avatar_url;
      }
      resolve(avatarUrl);
    });
  })
}
function getReactAvatar() {
  return new Promise((resolve, reject) => {
    github.search.users({ q: 'react' }, (err, res) => {
      if (err) {
        reject(err);
        return;
      }
      let avatarUrl = '';
      if (res.data && res.data.items) {
        avatarUrl = res.data.items[0].avatar_url;
      }
      resolve(avatarUrl);
    });
  })
}

Это очень простые функции, основанные на обещаниях, которые используют пакет Github NPM для получения аватара пользователя с сервера Github.

Замените промисы на асинхронное ожидание

Чтобы вызвать вышеупомянутые функции с промисами, мы должны сделать:

getPentaCodeAvatar()
  .then((result) => {
    console.log(result);
  })
  .catch((e) => {
    console.error('Error in getPentaCodeAvatarFunction (Promise Based)', e);
  });

Синтаксис чистый и простой, определенно лучше, чем вложенные обратные вызовы. Ошибка обнаружена в функции .catch(). Что меня беспокоит, так это многочисленные анонимные функции, передаваемые в .then() и .catch(), которые все еще не так чисты, как хотелось бы.

Давайте посмотрим, как мы можем сделать это с помощью async/await:

async function start() {
  const avatarUrl = await getPentaCodeAvatar();
  console.log(avatarUrl);
}
start();

Что это за магия? Вы говорите, что этот код выглядит синхронно, ну, это и есть цель, мы сначала обработаем слово function ключевым словом async, это сообщит компилятору, что следующая функция использует асинхронные вызовы чего-то, затем мы добавьте ключевое слово await перед функцией обещания getPentaCodeAvatar(), это заставит функцию ждать, пока асинхронный код внутри getPentaCodeAvatar() не выполнит закончите перед установкой результата в переменную avatarUrl. Имейте в виду, что вы можете использовать ключевое слово await только внутри функции с пометкой async, иначе вы получите синтаксическую ошибку от node.

Обработка ошибок

С промисами вы можете перехватывать ошибки с помощью функции .catch(), но как это сделать с помощью Async/Await? Ответ: try…catch, вы просто заключаете код в try…catch, и ошибки будут обнаружены:

async function start() {
  try {
    const avatarUrl = await getPentaCodeAvatar();
    console.log(avatarUrl);
  } catch (e) {
    console.error('Error in getPentaCodeAvatarFunction (Async Await Based)', e);
  }
}
start();

Хорошая вещь в этом заключается в том, что вы сможете отлавливать ошибки внутри функции обещания И что-либо внутри блока try, например ошибку JSON.parse. IMO, это намного мощнее и знакомо среднему разработчику, недостатком этого является то, что вам придется писать много попыток/уловов.

Синхронный

Допустим, вам нужно вызвать две или три асинхронные функции и вам нужны результаты предыдущих вызовов, чтобы выполнить следующую, другими словами, синхронные вызовы, тогда следующий пример покажет вам, как это сделать:

async function startSynchronous() {
  try {
    const pentaCodeAvatarUrl = await getPentaCodeAvatar();
    const reactAvatarUrl = await getReactAvatar();
    const totalURL = pentaCodeAvatarUrl + reactAvatarUrl;
    console.log(totalURL);
  } catch (e) {
    console.error('Error in startSynchronous (Async Await Based)', e);
  }
}
startSynchronous();

Это очень похоже на обычные вызовы и присваивания функций: вы вызываете getPentaCodeAvatar() и получаете значение результата, сохраненное в переменной, затем вызываете getReactAvatar() и сохраняете результат. в том, что в другой переменной, и, наконец, вы объединяете их вместе в другой переменной. async/await упрощает понимание и позволяет безболезненно манипулировать переменными из различных асинхронных функций.

Параллельно

А если вы хотите, чтобы набор асинхронных функций выполнялся одновременно и параллельно?

async function startParallel() {
  try {
    let [ pentaCodeAvatarUrl, reactAvatarUrl ] = await Promise.all([getPentaCodeAvatar(), getReactAvatar()]);
    console.log(pentaCodeAvatarUrl, reactAvatarUrl)
  } catch (e) {
    console.error('Error in startParallel (Async Await Based)', e);
  }
}
startParallel();

Правильно, вы можете обернуть их в Promise.all и использовать перед ним ключевое слово await. Это МОЩНО, потому что оно сочетает в себе возможности async/await и промисов для выполнения того, что мы хотим, и в процессе делает код очень простым для понимания.

Я надеюсь, что эти четыре примера объяснили силу async/await, и я надеюсь, что вы сможете начать включать их в свои проекты.

Эта история была перепечатана на Medium. Оригинал истории можно найти на https://www.penta-code.com/blazing-fast-static-reactjs-site-with-gatsby-js