JavaScript async/await
Прежде чем объяснять async/await, нам нужно понять концепции асинхронного программирования в JavaScript; вы можете обратиться к:Асинхронное программирование в JavaScript。
JavaScript — однопоточный язык, то есть он может выполнять только одну задачу за раз. Чтобы избежать блокировки главного потока длительными задачами, JavaScript использует модель асинхронного программирования.
Асинхронное против синхронного
- Синхронное программирование: Код выполняется последовательно, и следующая операция запускается только после завершения предыдущей.
- Асинхронное программирование: Некоторые операции помещаются в "очередь задач". Главный поток продолжает выполнять последующий код и обрабатывает задачи в очереди, когда главный поток свободен.

Примеры
console.log('1');
console.log('2');
// Вывод: 1, 2
// Пример асинхронного выполнения
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// Вывод: 1, 3, 2
Проблема с функциями обратного вызова
До async/await в JavaScript в основном использовались функции обратного вызова для обработки асинхронных операций, но это могло привести к «аду обратных вызовов».
Примеры
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
console.log(d);
});
});
});
});
Эта вложенная структура затрудняет чтение и поддержку кода.
Введение в Promise
ES6 представил объект Promise для решения проблемы ада обратных вызовов.
Примеры
return new Promise((resolve, reject) => {
// Асинхронная операция
setTimeout(() => resolve('data'), 1000);
});
}
getData()
.then(data => {
console.log(data);
return getMoreData(data);
})
.then(moreData => {
console.log(moreData);
})
.catch(error => {
console.error(error);
});
Хотя Promise улучшил проблему колбэков, цепочечные вызовы then() всё ещё недостаточно интуитивны.
Синтаксис async/await
ES2017 ввёл async/await, который основан на Promise и заставляет асинхронный код выглядеть как синхронный.
async функции
Добавьте ключевое слово async перед объявлением функции, чтобы указать, что функция является асинхронной:
// Тело функции
}
async функции всегда возвращают Promise:
- Если возвращаемое значение не является Promise, оно автоматически оборачивается в разрешённый Promise
- Если выбрасывается исключение, оно возвращает отклонённый Promise
await выражения
await можно использовать только внутри асинхронных функций:
const result = await somePromise;
console.log(result);
}
await приостанавливает выполнение асинхронной функции до тех пор, пока Promise не завершится:
- Если Promise разрешён, он возвращает разрешённое значение
- Если Promise отклонён, он выбрасывает ошибку (которую можно перехватить с помощью try/catch)
Примеры практического применения
Базовое использование
Примеры
return new Promise(resolve => setTimeout(resolve, ms));
}
async function showMessage() {
console.log('Старт');
await delay(1000);
console.log('Через 1 секунду');
await delay(1000);
console.log('Ещё через 1 секунду');
}
showMessage();
Обработка ошибок
Примеры
try {
const response = await fetch('https://api.example.com/user');
if (!response.ok) {
throw new Error('Сетевой ответ не ok');
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Не удалось получить данные:', error);
}
}
Параллельное выполнение
Если вам нужно выполнить несколько асинхронных операций параллельно, вы можете использовать Promise.all:
Примеры
const [userData, productData] = await Promise.all([
fetch('/api/user'),
fetch('/api/products')
]);
const user = await userData.json();
const products = await productData.json();
return { user, products };
}
Распространённые проблемы и лучшие практики
1. Не забывайте await
Примеры
async function example() {
const data = fetch('/api'); // Пропущен await
console.log(data); // Выводит объект Promise
}
// Правильный пример
async function example() {
const data = await fetch('/api');
console.log(data); // Выводит фактические данные
}
2. Избегайте ненужного async
Примеры
async function unnecessaryAsync() {
return 42;
}
// Более простой способ записать это
function simpleFunction() {
return 42;
}
3. await верхнего уровня
await можно использовать напрямую на верхнем уровне модуля (возможность ES2022):
Примеры
const data = await fetch('/api');
console.log(data);
4. Вопросы производительности
- Последовательное выполнение против параллельного: используйте Promise.all должным образом для повышения производительности
- Обработка ошибок: убедитесь, что перехвачены все возможные ошибки
async/await против традиционного Promise
| Характеристика | async/await | Promise then/catch |
|---|---|---|
| Читаемость | Высокая, как в синхронном коде | Средняя, цепочки вызовов |
| Обработка ошибок | Использует try/catch | Использует .catch() |
| Отладка | Проще, с понятным стеком вызовов | Сложнее, стек вызовов может быть неясным |
| Структура кода | Более плоская | Вложенная или с цепочками |
Итог
async/await — значительное улучшение в асинхронном программировании на JavaScript. Он:
- Делает асинхронный код более читаемым и поддерживаемым
- Основан на Promise и совместим с существующим кодом на Promise
- Предоставляет более интуитивный способ обработки ошибок
- Улучшает процесс отладки
Хотя async/await не полностью заменяет Promise, в большинстве случаев он предоставляет более элегантное решение. Понимание того, как работает async/await, и его лучших практик значительно улучшит ваши навыки асинхронного программирования на JavaScript.
Другие расширения