От автора: Async / await — это новый способ написания асинхронного кода. Он построен на основе promises, поэтому он также является не блокирующим. Разница заключается в том, что асинхронный код выглядит и ведет себя немного больше как синхронный. Вот в чем вся его сила.
Наряду с написанием этой статьи я также создал видео на YouTube! Вы можете смотреть его и кодировать во время просмотра. Советую сначала прочитать статью, а затем написать код, руководствуясь видео.Ссылка на видео: Изучаем Async / Await на реальном проекте.
Предыдущими вариантами асинхронного кода были обратные вызовы и promises.
Обратные вызовы в действии
1 2 3 |
setTimeout(() => { console.log('This runs after 1000 milliseconds.'); }, 1000); |
Проблема с обратными вызовами - печально известный ад обратных вызовов
Вложенные обратные вызовы внутри обратных вызовов скоро начнут выглядеть так:
Ад обратных вызовов
Ситуация, когда обратные вызовы вложены в другие обратные вызовы на несколько уровней, что потенциально затрудняет понимание и поддержку кода.
Promises в действии
1 2 3 4 5 6 7 8 9 10 11 |
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 возвращает Promise, который представляет процесс этой функции. Функция разрешения сообщает об окончании экземпляра Promise. После этого мы можем вызвать для этой функции .then() и .catch():
then — запускает обратный вызов, который вы передали ему для случая, когда Promise выполнен.
catch - запускает обратный вызов, который вы передали ему для случая, когда что-то пошло не так.
Асинхронные функции
Асинхронная функция предоставляет нам чистый и лаконичный синтаксис, позволяющий писать меньше кода для достижения того же результата, который мы получили бы с помощью Promise. Async — не более чем синтаксический сахар для Promise. Асинхронные функции создаются путем добавления слова async перед объявлением функции следующим образом:
1 2 3 |
const asyncFunction = async () => { // Code } |
Асинхронные функции могут быть приостановлены с помощью await, ключевого слова, которое может быть использовано только внутри асинхронной функции. Await возвращает все, что возвращает асинхронная функция, когда она завершена. В этом разница между promises и async/await:
1 2 3 4 5 6 7 8 9 10 |
// 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.
В этом приложении мы собираемся получать данные из двух асинхронных источников:
Currency Layer - вам нужно бесплатно зарегистрироваться, чтобы использовать ключ доступа API. Этот API предоставит нам данные, необходимые для расчета обменного курса между валютами.
Rest Countries - этот API предоставит нам информацию о том, где мы можем использовать валюту, в которую только что перевели деньги.
Для начала создайте новый каталог и запустите npm init, пропустите все шаги и установите axios, введя npm i —save axios. Создайте новый файл с именем currency-converter.js.
Во-первых, установите зависимость axios, введя: const axios = require(‘axios’);
Давайте рассмотрим подробнее async/await
Наша цель состоит в том, чтобы получить три функции. Ни одну, ни две, а три асинхронные функции. Первая функция будет собирать данные о валютах. Вторая — данные о странах. И третья функция будет собирать эту информацию в одном месте и красиво отображать ее пользователю.
Первая функция — асинхронное получение данных валюты
Мы создадим асинхронную функцию, которая будет принимать два аргумента: fromCurrency и toCurrency.
1 |
const getExchangeRate = async (fromCurrency, toCurrency) => {} |
Теперь нам нужно получить данные. С помощью async / await мы можем назначать данные непосредственно переменной; не забудьте зарегистрироваться и ввести свой корректный ключ доступа.
1 2 3 |
const getExchangeRate = async (fromCurrency, toCurrency) => { const response = await axios.get('//data.fixer.io/api/latest? access_key=[yourAccessKey]&format=1'); } |
Данные из ответа доступны в разделе response.data.rates, так что мы можем поместить их в переменную чуть ниже ответа:
1 |
const rate = response.data.rates; |
Поскольку все конвертируется из евро, ниже мы создадим переменную с именем euro, которая будет равна 1 / на валюту, из которой мы хотим конвертировать:
1 |
const euro = 1 / rate[fromCurrency]; |
Наконец, чтобы получить обменный курс, мы можем умножить евро на валюту, в которую мы хотим конвертировать:
1 |
const exchangeRate = euro * rate[toCurrency]; |
Функция должна выглядеть примерно так:
Вторая функция — асинхронный прием данных страны
Мы создадим асинхронную функцию, которая будет принимать в качестве аргумента currencyCode:
1 |
const getCountries = async (currencyCode) => {} |
Мы собираемся извлечь данные и присвоить их переменной:
1 |
const response = await axios.get(`//restcountries.eu/rest/v2/currency/${currencyCode}`); |
Затем мы сопоставим данные и вернемся к каждому country.name:
1 |
return response.data.map(country => country.name); |
В конце функция должна выглядеть примерно так:
Третья и последняя функция — объединение всего вместе
Мы создадим асинхронную функцию, которая будет принимать в качестве аргументов fromCurrency, toCurrency и сумму:
1 |
const convert = async (fromCurrency, toCurrency, amount) => {} |
Сначала мы получаем данные валюты:
1 |
const exchangeRate = await getExchangeRate(fromCurrency, toCurrency); |
Во-вторых, мы получаем данные страны:
1 |
const countries = await getCountries(toCurrency); |
В-третьих, мы сохраняем конвертированную сумму в переменную:
1 |
const convertedAmount = (amount * exchangeRate).toFixed(2); |
Наконец, мы выводим все это пользователю:
1 |
return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`; |
Все это вместе должно выглядеть так:
Добавление try / catch для устранения ошибок
Нам нужно обернуть всю нашу логику в try и перехватить ошибку, если она есть:
1 2 3 4 5 6 7 8 9 10 11 |
const getExchangeRate = async (fromCurrency, toCurrency) => { try { const response = await axios.get('//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}`); } }; |
Повторите то же самое для второй функции:
1 2 3 4 5 6 7 8 |
const getCountries = async (currencyCode) => { try { const response = await axios.get(`//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}`); } }; |
А поскольку третья функция просто работает с тем, что предусмотрено первой и второй функциями, в ней нет необходимости проверять ошибки. Наконец, мы можем вызвать функцию и получить данные:
1 2 3 4 5 6 |
convertCurrency('USD', 'HRK', 20) .then((message) => { console.log(message); }).catch((error) => { console.log(error.message); }); |
Результат:
Вот и все!
Мы закончили! Если вы что-то не уловили, код доступен в этом хранилище.
Автор: Adrian Hajdin
Источник: //medium.freecodecamp.org/
Редакция: Команда webformyself.