Асинхронные функции
1. Введение
Работа с бекендом может быть довольно сложной, после одной асинхронной операции необходимо сделать еще один запрос на сервер по полученным данным и так несколько раз.
Представьте следующую ситуацию:
Нам необходимо запросить профиль пользователя от
/user-profile
После того как мы получили профиль, нам необходимо запросить всех друзей этого пользователя от
/users/:userId/friends
Мы можем запрашивать данные только одного из друзей за одно обращение к серверу, допустим
/users/:userId
И только после того как мы запросили всех друзей, мы можем отобразить это пользователю.
Реализация такого алгоритма псевдокодом на промисах может выглядеть следующим образом:
fetch('/user-profile')
.then(user => fetch(`/users/${user.id}/friends`))
.then(idList => {
const friends = idList.map(id => fetch(`/users/${id}`));
return Promise.all(friends);
})
.then(friends => console.log(friends))
.catch(error => console.error(error));
Copy
Согласитесь, не самый легкочитаемый код, при том, что операции очень простые. Внутри then
мы передаем функции-обработчики и в результате получаются елки вложенности. Вот бы можно было сделать так, что наш код выглядел следующим образом.
const promise = fetch('/users');
/*
* Как-то заставить JS подождать пока выполнится промис
* После чего просто использовать данные, без then и т. д.
*/
console.log(promise.result);
Copy
Но проблема в том, что JavaScript не многопоточный язык, поэтому любое ожидание в коде блокирует весь скрипт. Например когда мы используем alert
и все виснет до момента выхода из него. На помощь приходят генераторы.
2. Асинхронные функции
Асинхронные функции (async/await) - прослойка над промисами и генераторами, которая абстрагирует сложности их использования и предоставляет удобный интерфейс.
Для создания асинхронной функции перед function
добавляем ключевое слово async
. После чего, внутри такой функции мы можем использовать оператор await
и справа от await
поставить что-то, что вернет промис. Всегда в таком порядке await -> promise
.
const getUsers = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = response.json();
return users;
};
getUsers().then(users => console.log(users));
Copy
При такой записи, await
приостановит только эту функцию (не весь скрипт) и будет ждать, пока не выполнится промис. Как только промис выполнился - исполнение функции возобновляется и на строке ниже нам доступен результат асинхронной операции.
Асинхронная функция всегда возвращает промис. Даже если написать
return 'hello'
, строка будет обернута в промис, который резолвится с этой строкой.Оператор
await
приостанавливает функцию до того момента, когда промис выполнился, то есть перешел в состояниеSettled
Оператор
await
или вернет значение успешного(Fullfilled
) промиса, или выбросит ошибку в случае если промис был отклонен(Rejected
)Оператор
await
можно использовать только в теле асинхронной функции.Можно использовать любые методы промисов внутри асинхронной функции, к примеру
Promise.all
Для того чтобы обработать ошибку используется конструкция try/catch
- аналог блока catch
.
const getUsers = async () => {
try {
const result = await fetch('https://jsonplaceholder.typicode.com/users');
console.log(result);
} catch (err) {
throw err;
}
};
getUsers()
.then(users => console.log(users))
.catch(error => console.log(error));
Copy
Не забывайте ставить
await
перед промисомНе используйте
await
в методах вродеmap
,filter
и т. д., работает не так как можно ожидатьХотя код выглядит синхронным, не забывайте, что исполнение кода в такой функции приостанавливается и возобновляется с течением времени.
Теперь перепишем наш код, используя асинхронную функцию.
const getUserFriends = async () => {
const user = await fetch('/user-profile');
const idList = await fetch(`/users/${user.id}/friends`);
const promises = idList.map(id => fetch(`/users/${id}`));
const friends = await Promise.all(promises);
return friends;
};
// Асинхронная функция всегда вернет промис
const promise = getUserFriends();
promise.then(friends => console.log(friends));
Copy
3. Дополнительные материалы
Last updated