Перед вами перевод статьи Adrian Hajdin, которая была опубликована на сайте freeCodeCamp. Под катом автор понятно и лаконично объясняет, в чем преимущество async/await, и на конкретном примере показывает, как использовать этот синтаксис.



На заметку…

Я не только написал эту статью, но и создал видео на YouTube!

Вы можете следовать инструкциям в ролике и программировать в процессе просмотра. Я советую сначала прочесть статью и уже потом писать код по ходу видео.

Ссылка на видео: Learn Async/Await in This Real World Project

Введение


Async/await — это новый способ написания асинхронного кода. Этот синтаксис построен поверх промисов и поэтому не является блокирующим.

Отличие от других способов создания асинхронного кода в том, что по виду и поведению асинхронный код, который реализован через async/await, напоминает синхронный. И в этом его преимущество.

Предыдущими способами организации асинхронного кода были функции обратного вызова (callbacks) и промисы (promises).

Функции обратного вызова в действии


setTimeout(() => {
  console.log('This runs after 1000 milliseconds.');
}, 1000);

Проблема функций обратного вызова — пресловутый «ад обратных вызовов» (Callback Hell)


При вложении функций обратного вызова друг в друга код вскоре начинает выглядеть вот так:


«Ад обратных вызовов» (Callback Hell)

Ад обратных вызовов

Это ситуация, при которой обратные вызовы вкладываются в другие обратные вызовы в несколько уровней, из-за чего бывает трудно понимать код и управлять им.

Промисы в действии


const promiseFunction = new Promise((resolve, reject) => {
  const add = (a, b) => a + b;
  resolve(add(2, 2));
});
promiseFunction.then((response) => {
  console.log(response);
}).catch((error) => {
  console.log(error);
});

promiseFunction возвращает промис, который представляет процесс этой функции. Функция resolve дает объекту Promise понять, что процесс завершен.

Затем мы можем вызвать .then() и .catch() для этой функции промиса:
then — запускает обратный вызов, который вы передаете, когда промис завершен.
catch — запускает обратный вызов, который вы передаете, когда что-то пошло не так.

Функции async


Функции async предоставляют нам чистый и лаконичный синтаксис, который позволяет писать меньше кода и получать такой же результат, как и при использовании промисов. Async — это не что иное, как синтаксический сахар для промисов.

Функции async создаются путем добавления ключевого слова async перед объявлением функции, например:

const asyncFunction = async () => {
  // Code
}

Выполнение асинхронных функций можно приостановить с помощью await — ключевого слова, которое используется только внутри функции async. Await возвращает все, что возвращает функция async при ее выполнении.

Различие между промисами и async/await:

// Async/Await
const asyncGreeting = async () => 'Greetings';
// Promises
const promiseGreeting = () => new Promise(((resolve) => {
  resolve('Greetings');
}));
asyncGreeting().then(result => console.log(result));
promiseGreeting().then(result => console.log(result));

Async/await похож на синхронный код, который гораздо легче понять.

Теперь, когда мы осветили основные принципы, давайте перейдем к реальному примеру использования!

Конвертер валют


Разъяснение и настройка проекта


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

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

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

  1. Currency Layerhttps://currencylayer.com — вам нужно будет бесплатно зарегистрироваться, чтобы воспользоваться ключом доступа API. Он предоставит нам данные, необходимые для расчета курса обмена валют.
  2. Rest Countrieshttp://restcountries.eu/ — этот API предоставит информацию о том, где мы можем воспользоваться валютой, в которую только что перевели свои деньги.

Прежде всего создайте новую папку и запустите npm init, пропустите все шаги и установите axios, введя npm i -- save axios. Создайте новый файл под названием currency-converter.js.

Сначала запросите axios, введя: const axios = require(‘axios’);

Давайте погрузимся в async/await


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

Первая функция — асинхронное получение данных о валютах

Мы создадим асинхронную функцию, которая будет включать два аргумента — fromCurrency и toCurrency.

const getExchangeRate = async (fromCurrency, toCurrency) => {}

Теперь нужно получить данные. Используя async/await, можно назначить данные напрямую переменной. Не забудьте зарегистрироваться и ввести свой ключ доступа.

const getExchangeRate = async (fromCurrency, toCurrency) => {
  const response = await axios.get('http://data.fixer.io/api/latest?    access_key=[yourAccessKey]&format=1');
}

Данные от ответа доступны в response.data.rates, поэтому можно вставить это выражение в переменную прямо под response:

const rate = response.data.rates;

Поскольку все конвертируется из евро, ниже мы создадим переменную под названием euro, которая будет равна 1/валюты, из которой мы хотим перевести деньги:

const euro = 1 / rate[fromCurrency];

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

const exchangeRate = euro * rate[toCurrency];

В результате функция должна выглядеть примерно вот так:



Вторая функция — асинхронное получение данных о стране

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

const getCountries = async (currencyCode) => {}

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

const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);

Затем мы установим соответствие в данных и вернем country.name для каждого кода валюты:

return response.data.map(country => country.name);

В результате функция должна выглядеть примерно вот так:



Третья и последняя функция — объединяем все вместе
Мы создадим асинхронную функцию, которая будет включать fromCurrency, toCurrency и сумму в качестве аргументов:

const convert = async (fromCurrency, toCurrency, amount) => {}

Сначала получим данные о валюте:

const exchangeRate = await getExchangeRate(fromCurrency, toCurrency);

Затем данные о странах:

const countries = await getCountries(toCurrency);

После этого сохраним конвертированную сумму в переменную:

const convertedAmount = (amount * exchangeRate).toFixed(2);

В итоге мы выводим все это пользователю:

return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`;

Все вместе это должно выглядеть вот так:



Добавление оператора try/catch для обработки ошибок
Нам нужно обернуть нашу логику блоком try/catch, чтобы отловить ошибки, если они есть:

const getExchangeRate = async (fromCurrency, toCurrency) => {
  try {
    const response = await       axios.get('http://data.fixer.io/api/latest?access_key=f68b13604ac8e570a00f7d8fe7f25e1b&format=1');
    const rate = response.data.rates;
    const euro = 1 / rate[fromCurrency];
    const exchangeRate = euro * rate[toCurrency];
    return exchangeRate;
  } catch (error) {
    throw new Error(`Unable to get currency ${fromCurrency} and  ${toCurrency}`);
  }
};

Проделайте то же самое со второй функцией:

const getCountries = async (currencyCode) => {
  try {
    const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);
return response.data.map(country => country.name);
  } catch (error) {
    throw new Error(`Unable to get countries that use ${currencyCode}`);
  }
};

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

В результате мы можем вызвать функцию и получить данные:

convertCurrency('USD', 'HRK', 20)
  .then((message) => {
    console.log(message);
  }).catch((error) => {
    console.log(error.message);
  });

Результат, который вы получите:



Вот и все!


Вы проделали весь путь до конца! Если у вас что-то не получилось в процессе, вы можете найти исходный код в этом репозитории. Если у вас есть вопросы или вы хотите оставить отзыв, напишите комментарий. Для меня самой большой помощью была бы ваша поддержка на YouTube, потому что я совсем недавно создал там канал! Кликайте сюда — скоро здесь появится много чего интересного! :)

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

Комментарии (2)


  1. akdes
    31.01.2019 17:02
    +1

    Как бы уже было…
    тыц


  1. donRumatta
    31.01.2019 23:08

    Было, но перевод здесь кажется более литературным. Оформление зато у RUVDS приятнее глазу(=