В свое время “промисы” (объекты promise) сильно облегчили нашу жизнь, избавив от написания асинхронных запросов с использованием коллбэков. “Промисы” также используются конструкцией async wait, которым посвящена эта статья.
Объекты promise можно представить себе, как что-то, что мы получаем не сразу, а через какое-то время.
Примеры таких объектов:
AJAX запросы на получение данных
или получение доступа к web камере пользователя, после клика на кнопку “разрешить”
При этом весь последующий код не блокируется в ожидании завершения выполнения асинхронного запроса, а продолжает выполняться в обычном режиме.
Весь код, завязанный на получаемые в асинхронном запросе данные, продолжает выполняться только после получения этих данных.
Пример 1.
Представим, что приготовление нашего завтрака разбито на действия:
Готовим кофе
Пьем кофе
Готовим тосты
Едим тосты
Моем посуду
Мы выполняем каждое действие без жесткой последовательности. Например, начинаем пить кофе пока готовятся наши тосты. То есть, приготовление тостов не блокирует нашу возможность начать пить кофе.
В нашем случае приготовление тостов - промис, результат которого мы получим, когда сработает таймер на тостере.
Во времена 'коллбэков', реализация нашего примера имела бы следующий вид (это – то, что называется callback hell):
**Промисы** позволяют написать тоже самое более **просто** и понятно. Мы **запускаем** нужные нам процессы и получаем в ответ **обещания** (объекты Promise).
Plain Text const coffeePromise = makeCoffe(); const breakfastPromise = makeBreakfast();
Через **какое-то время** получаем результат (запрашиваемые данные). **По мере** получения результатов (данных) наших действий, **продолжаем** завтракать (пить кофе и есть тосты).
Plain Text // Промис на чашку кофе coffeePromise.then((response) => { // Пьем готовый кофе drinkCoffee(); });
Можно дождаться пока **получим** ответ для **обоих** объектов `promise`, и продолжить завтрак с уже приготовленным **кофе** и **тостом**.
Plain Text const coffeePromise = makeCoffe(); const breakfastPromise = makeBreakfast();
Plain Text fetch('https://github.com/vasilymur') .then((data) => data.json()) .then((vasily) => console.log(vasily));
**Асинхронные** запросы можно также выполнять с помощью библиотеки **Axios**, которая работает в браузере и также возвращает **promise**.
Plain Text axios.get('https://github.com/vasilymur').then((vasily) => console.log(vasily));
Мы также можем легко **создать** наши собственные объекты **promise**.
const curWeather = getWeather(); curWeather.then((data) => { // Получаем данные через 3 сек. console.log(data); });
**Промисы** кажутся **отличным** решением для работы с **асинхронным** кодом, но есть несколько моментов, которые мне **не нравятся**:
- Весь **код**, завязанный на данные, которые мы **получаем** из “промиса” должен быть помещен **внутри** конструкции `.then()`
## Async Await JS
Конструкция `async await` также использует объекты **promise** для создания **асинхронных** запросов, но делает написание нашего кода заметно **проще**.
**Async await** позволяет писать **асинхронный** код, так как будто он является **синхронным**.
Для использования `async await` нужно указать, что наша функция будет содержать асинхронный код, путем добавления слова `async`.
Plain Text async function eatBreakfast() {}
Далее, **внутри** функции нужно **отметить** словом `await` те строчки, в которых содержится **асинхронный** код.
Plain Text // 1-й промис const getToast = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Ваш тост готов!'); }, 1000); }); };
Еще один вариант **обработки ошибок** в `async await` – **'добавлять'** `.catch` к каждой **отдельной** функции, возвращающей **промис**.
Plain Text async function loadCities() { const response = await fetch( 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/ities.json' ); const cities = await response.json(); console.log(cities); }
Более **продвинутый** вариант – вынести логику `catch` во **внешнюю** функцию (Higher Order Function).
Возьмем нашу **функцию**, которая обращается к **внешнему** API, для получения данных о городах:
Plain Text async function loadCities() { const response = await fetch( // адрес с ошибкой (citis вместо cities) 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/citis.json' ); const cities = await response.json(); }
Далее пишем **функцию**, которая будет **принимать** нашу функцию и возвращать ее же, но с **добавленным** `catch`, как в предыдущем примере:
Plain Text function handleError(fn) { console.log(fn); return function () { return fn().catch(function (err) { console.log('Ошибка!!', err); }); }; }
Далее мы можем использовать следующую **конструкцию**, чтобы **обернуть** нашу функцию `loadCities()` в функцию `handleError()`
Plain Text const getCitiesWithErrorHandler = handleError(loadCities); getCitiesWithErrorHandler(); ```
Таким образом мы сделаем наш код более простым и менее громоздким.