Глава 4.5: Async/await vs Promise
Время изучения: 30 минут
1. Асинхронность: Два способа управления "космической связью"
Представьте, что ЦУП отправляет команду на Марс. Ответ придет только через несколько минут. Как организовать работу в это время?
Способ 1: "Протокол обратного вызова" (Promise с .then()
)
Вы отправляете команду и даете инструкцию: "КОГДА придет ответ, ТОГДА выполни вот эту функцию". Это похоже на цепочку событий.
Способ 2: "Режим ожидания" (Async/await) Вы говорите: "Я ПОДОЖДУ ответа на эту команду, но не буду блокировать другие пульты управления". Вы как бы ставите выполнение этой конкретной задачи на паузу, позволяя остальному ЦУПу работать.
Оба способа решают одну и ту же задачу — управление асинхронными операциями. async/await
— это просто более современный и читаемый синтаксис, который работает "поверх" промисов.
💡 Космическая аналогия:
- Promise с
.then()
: Это как написать на стикере: "Когда марсоход пришлет фото, передать его в отдел анализа".- Async/await: Это как сказать ассистенту: "Подожди фото от марсохода, а я пока займусь расчетами для запуска новой ракеты".
2. Promise с .then()
: Классическая цепочка команд
Это фундаментальный способ работы с асинхронностью в JavaScript, который мы использовали в главе 4.1.
Вспомним наш первый код:
function getIssPositionWithPromises() {
console.log('Отправляю запрос по протоколу "Promise"...');
fetch('http://api.open-notify.org/iss-now.json')
.then(response => {
// Этап 1: Получен ответ
if (!response.ok) {
throw new Error(`Ошибка HTTP: ${response.status}`);
}
return response.json(); // Возвращаем новый промис
})
.then(data => {
// Этап 2: Данные распарсены
console.log('Данные по протоколу "Promise" получены:', data.iss_position);
})
.catch(error => {
// Этап 3 (Ошибка): Что-то пошло не так на любом из этапов
console.error('Сбой связи по протоколу "Promise":', error);
});
console.log('...команда отправлена, ЦУП продолжает работу...');
}
Плюсы:
- Явная цепочка действий.
- Хорошо подходит для простых последовательных операций.
Минусы:
- "Ад обратных вызовов" (Callback Hell): При большом количестве вложенных асинхронных операций код может превратиться в "лесенку" из
.then()
, которую сложно читать. - Обработка ошибок может быть менее интуитивной.
3. Async/await: Современный синхронный стиль
async/await
— это "синтаксический сахар" над промисами, который позволяет писать асинхронный код так, будто он синхронный.
Правила использования:
- Ключевое слово
await
можно использовать только внутри функции, помеченной какasync
. await
ставится перед вызовом, который возвращает промис (например,fetch()
илиresponse.json()
).await
"приостанавливает" выполнениеasync
-функции до тех пор, пока промис не будет разрешен, и возвращает его результат.
Тот же код, переписанный с async/await
:
async function getIssPositionWithAsyncAwait() {
console.log('Отправляю запрос по протоколу "Async/await"...');
try {
// Этап 1: Ждем ответ от сервера
const response = await fetch('http://api.open-notify.org/iss-now.json');
if (!response.ok) {
throw new Error(`Ошибка HTTP: ${response.status}`);
}
// Этап 2: Ждем, пока тело ответа будет преобразовано в JSON
const data = await response.json();
console.log('Данные по протоколу "Async/await" получены:', data.iss_position);
} catch (error) {
// Этап 3 (Ошибка): Ловим любую ошибку из блока try
console.error('Сбой связи по протоколу "Async/await":', error);
}
console.log('...команда отправлена, ЦУП продолжает работу...');
}
Плюсы:
- Читаемость: Код выглядит почти как обычный синхронный код, его легко читать сверху вниз.
- Обработка ошибок: Используется стандартный и привычный блок
try...catch
. - Отладка: Гораздо проще отлаживать, так как можно ставить точки останова (breakpoints) на каждой строке с
await
.
Минусы:
- Легко забыть
await
илиasync
, что приведет к ошибкам.
4. Когда какой протокол использовать?
Ситуация | Рекомендуемый подход | Почему? |
---|---|---|
Большинство случаев | async/await |
Код чище, проще читать и отлаживать. Это современный стандарт. |
Простая цепочка из 1-2 действий | Promise с .then() |
Вполне подходит, код остается компактным. |
Параллельное выполнение нескольких запросов | Promise.all() |
Этот метод позволяет запустить несколько промисов одновременно и дождаться, пока все они выполнятся. async/await отлично с ним сочетается. |
Пример с Promise.all()
:
async function getParallelData() {
try {
// Запускаем оба запроса одновременно
const [shipsResponse, launchesResponse] = await Promise.all([
fetch('https://api.spacexdata.com/v4/rockets'),
fetch('https://api.spacexdata.com/v4/launches/latest')
]);
if (!shipsResponse.ok || !launchesResponse.ok) {
throw new Error('Один из запросов провалился!');
}
const rockets = await shipsResponse.json();
const latestLaunch = await launchesResponse.json();
console.log(`Всего ракет в флоте: ${rockets.length}`);
console.log(`Последний запуск: ${latestLaunch.name}`);
} catch (error) {
console.error('Ошибка при получении параллельных данных:', error);
}
}
Квиз для закрепления
🚀 Итог главы:
Вы изучили два синтаксиса для управления асинхронными операциями и поняли, почему async/await
является предпочтительным в большинстве современных проектов.
- 🔗 Вы освежили знания о Promise с
.then()
. - 🛠️ Вы глубоко поняли, как работает
async/await
и его преимущества. - ⚡ Вы узнали о
Promise.all
для выполнения параллельных запросов.
Протоколы связи изучены! В финальной главе этого раздела мы соберем все наши знания воедино и достроим наш "Центр Управления Полетами", создав полноценный интерфейс для всех CRUD-операций.
📌 Практика:
- Перепишите все функции в вашем
app.js
, которые еще используют.then()
, на синтаксисasync/await
.- Попробуйте добавить еще один запрос в
Promise.all()
(например, кhttps://api.spacexdata.com/v4/starlink
) и вывести данные.⚠️ Если ошибки:
await is only valid in async functions
: Убедитесь, что функция, где вы используетеawait
, помечена какasync
.- Переменная содержит
[object Promise]
: Вы забыли поставитьawait
перед функцией, возвращающей промис.