Async/await — это синтаксический сахар над промисами в JavaScript, который позволяет писать асинхронный код так, как будто он синхронный. Ключевое слово async перед функцией делает её асинхронной и гарантирует возврат промиса, а await приостанавливает выполнение функции до разрешения промиса. Это упрощает чтение и написание асинхронного кода, избегая цепочек .then().
Основные преимущества:
Async/await — это современный способ работы с асинхронным кодом в JavaScript, представленный в ES2017. Он позволяет писать асинхронный код, который выглядит и ведет себя как синхронный, делая его более читаемым и поддерживаемым.
Ключевые слова async и await всегда используются вместе:
// Объявление асинхронной функции
async function fetchData() {
// await приостанавливает выполнение до разрешения промиса
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// Использование
fetchData()
.then(result => console.log('Данные:', result))
.catch(error => console.error('Ошибка:', error));Async/await особенно удобен при работе с API:
// С промисами
function getUserDataWithPromises(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${userId}/posts`)
.then(response => response.json())
.then(posts => ({ user, posts }));
})
.catch(error => console.error('Ошибка:', error));
}
// С async/await
async function getUserDataWithAsync(userId) {
try {
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
const postsResponse = await fetch(`/api/users/${userId}/posts`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('Ошибка:', error);
throw error;
}
}Когда операции должны выполняться одна за другой:
async function processUserData() {
try {
// Шаг 1: Получаем данные пользователя
const user = await fetchUser();
console.log('Пользователь получен:', user.name);
// Шаг 2: Получаем профиль пользователя
const profile = await fetchProfile(user.id);
console.log('Профиль получен:', profile.email);
// Шаг 3: Получаем настройки пользователя
const preferences = await fetchPreferences(profile.id);
console.log('Настройки получены:', preferences.theme);
return { user, profile, preferences };
} catch (error) {
console.error('Ошибка при обработке данных пользователя:', error);
throw error;
}
}Иногда нужно выполнить несколько независимых операций параллельно:
// Последовательное выполнение (медленнее)
async function sequentialExecution() {
const users = await fetchUsers(); // 1 секунда
const posts = await fetchPosts(); // 1 секунда
const comments = await fetchComments(); // 1 секунда
// Общее время: ~3 секунды
return { users, posts, comments };
}
// Параллельное выполнение (быстрее)
async function parallelExecution() {
const [users, posts, comments] = await Promise.all([
fetchUsers(), // 1 секунда
fetchPosts(), // 1 секунда
fetchComments() // 1 секунда
]);
// Общее время: ~1 секунда
return { users, posts, comments };
}async function handleSubmit(formData) {
try {
// Валидация данных
const validation = await validateForm(formData);
if (!validation.isValid) {
throw new Error('Форма содержит ошибки');
}
// Отправка данных
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Ошибка отправки данных');
}
const result = await response.json();
console.log('Форма успешно отправлена:', result);
return result;
} catch (error) {
console.error('Ошибка при отправке формы:', error);
showErrorToUser(error.message);
}
}async function loadUserData(userId) {
try {
// Сначала проверяем кэш
const cachedData = localStorage.getItem(`user_${userId}`);
if (cachedData) {
console.log('Данные загружены из кэша');
return JSON.parse(cachedData);
}
// Если нет в кэше, загружаем из API
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Ошибка загрузки данных пользователя');
}
const userData = await response.json();
// Сохраняем в кэш
localStorage.setItem(`user_${userId}`, JSON.stringify(userData));
return userData;
} catch (error) {
console.error('Ошибка загрузки данных пользователя:', error);
throw error;
}
}async function fetchUserDataFromMultipleSources(userId) {
try {
// Загружаем данные из основного API
const mainDataPromise = fetch(`/api/users/${userId}`).then(res => res.json());
// Загружаем данные из резервного API
const backupDataPromise = fetch(`/backup-api/users/${userId}`).then(res => res.json());
// Ждем первый успешный результат
const userData = await Promise.any([mainDataPromise, backupDataPromise]);
return userData;
} catch (error) {
if (error instanceof AggregateError) {
console.error('Все источники данных недоступны:', error.errors);
} else {
console.error('Ошибка загрузки данных:', error);
}
throw error;
}
}// ❌ Забытый await
async function badExample() {
const data = fetch('/api/data'); // Это промис, а не данные!
console.log(data.name); // undefined
}
// ✅ Правильное использование await
async function goodExample() {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data.name); // реальные данные
}// ❌ Неправильная обработка ошибок
async function badErrorHandling() {
const data = await fetch('/api/data').json(); // Ошибка не будет поймана правильно
return data;
}
// ✅ Правильная обработка ошибок
async function goodErrorHandling() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP ошибка: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Ошибка загрузки данных:', error);
throw error;
}
}// ❌ Избыточное использование await
async function badChaining() {
const response = await fetch('/api/data');
const data = await response.json();
const processed = await processData(data);
const validated = await validateData(processed);
return validated;
}
// ✅ Цепочка без лишних await
async function goodChaining() {
const response = await fetch('/api/data');
const data = await response.json();
return validateData(processData(data));
}| Особенность | Промисы | Async/Await |
|---|---|---|
| Читаемость | Цепочки .then() | Линейный код |
| Обработка ошибок | .catch() | try/catch |
| Отладка | Сложнее из-за цепочек | Проще, сохраняется стек |
| Производительность | Одинаковая | Одинаковая |
| Совместимость | Все современные браузеры | Требует транспиляцию для старых браузеров |
Async/await — это мощный инструмент, который делает асинхронный код более читаемым и поддерживаемым. Понимание async/await критически важно для современной разработки на JavaScript.
Что будет выведено в консоль и почему? Исправьте код, если необходимо:
async function processData() {
console.log('1. Начало функции');
const data1 = await fetch('/api/data1');
console.log('2. После первого запроса');
const data2 = fetch('/api/data2');
console.log('3. После второго запроса');
const result1 = data1.json();
console.log('4. После парсинга первого ответа');
const result2 = await data2.json();
console.log('5. После парсинга второго ответа');
return { result1, result2 };
}
processData()
.then(results => console.log('6. Результаты:', results))
.catch(error => console.error('7. Ошибка:', error.message));Ответ: Код содержит несколько ошибок. Правильная версия:
async function processData() {
console.log('1. Начало функции');
const response1 = await fetch('/api/data1');
console.log('2. После первого запроса');
const response2 = await fetch('/api/data2'); // ДОБАВЛЕН await
console.log('3. После второго запроса');
const result1 = await response1.json();
console.log('4. После парсинга первого ответа');
const result2 = await response2.json();
console.log('5. После парсинга второго ответа');
return { result1, result2 };
}Объяснение ошибок:
Порядок выполнения:
Важно понимать, что await приостанавливает выполнение функции до разрешения промиса, но не блокирует весь поток выполнения.
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪