> Какой инструмент синтетического сахара используют для работы с асинхронным кодом вместо промисов (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Kaspersky

Стек: JavaScript

> Пример ответа

Короткий ответ

Инструмент синтетического сахара для работы с асинхронным кодом вместо промисов - это async/await. Он построен поверх промисов и предоставляет синтаксис, похожий на синхронный код, что улучшает читаемость и упрощает обработку ошибок через try/catch. Async/await не заменяет промисы, а является надстройкой, делающей асинхронный код более линейным и понятным.

Подробное объяснение

Async/await - это синтаксический сахар, введённый в ES2017, который позволяет писать асинхронный код в стиле, напоминающем синхронный. Ключевое слово async перед функцией делает её возвращающей Promise, а await приостанавливает выполнение функции до разрешения промиса, не блокируя event loop.

Основные преимущества перед чистыми промисами:

  • Устранение цепочек .then(), которые могут приводить к глубокой вложенности
  • Единообразная обработка ошибок через try/catch вместо .catch()
  • Возможность использовать условные операторы и циклы естественным образом
  • Упрощение отладки, так как стек вызовов становится более предсказуемым

Важно понимать, что async/await не заменяет промисы концептуально - каждый await работает с Promise, а async функция всегда возвращает Promise. Это надстройка, улучшающая ergonomics работы с асинхронностью.

На практике

В реальных проектах async/await используется повсеместно для:

  • HTTP-запросов (fetch, axios)
  • Работы с базами данных
  • Файловых операций
  • Таймеров и интервалов
  • Последовательного выполнения асинхронных операций

При этом важно помнить о trade-off: параллельные операции требуют Promise.all или Promise.allSettled, так как последовательный await замедляет выполнение. Также нужно избегать await в циклах, если операции независимы - используйте Promise.all с маппингом.

Пример кода

JAVASCRIPT
// Промисы
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(user => {
return fetch(`/api/posts?userId=${user.id}`)
.then(response => response.json())
})
.catch(error => console.error('Error:', error))
}
// Async/await
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
const postsResponse = await fetch(`/api/posts?userId=${user.id}`)
const posts = await postsResponse.json()
return posts
} catch (error) {
console.error('Error:', error)
}
}
// Параллельное выполнение с Promise.all
async function fetchMultipleUsers(ids) {
const promises = ids.map(id => fetch(`/api/users/${id}`).then(r => r.json()))
const users = await Promise.all(promises)
return users
}

Как отвечать на собеседовании

Начните с чёткого определения: async/await - синтаксический сахар над промисами. Подчеркните, что это не альтернатива, а надстройка. Объясните, как работает под капотом: async функция возвращает Promise, await приостанавливает выполнение в микротаске. Упомяните, что await можно использовать только внутри async функции (кроме top-level await в модулях). Приведите примеры, когда async/await упрощает код, и когда лучше использовать промисы напрямую (например, для параллельных операций). Покажите понимание обработки ошибок через try/catch и отличие от .catch().

Что проверяет интервьюер

  • Понимание, что async/await - это синтаксический сахар, а не новая концепция
  • Знание, как работает event loop в контексте await
  • Умение выбирать между последовательным и параллельным выполнением
  • Понимание обработки ошибок и отличий от промисов
  • Знание ограничений: await в циклах, top-level await, совместимость с thenable
  • Способность объяснить, как отлаживать async/await код

Типичные ошибки

  • Утверждение, что async/await полностью заменяет промисы - это неверно, так как под капотом используются промисы
  • Использование await для независимых операций последовательно вместо Promise.all, что замедляет выполнение
  • Забывание обернуть await в try/catch, что приводит к необработанным rejections
  • Использование await вне async функции без понимания top-level await
  • Предположение, что await блокирует event loop - на самом деле он приостанавливает только текущую async функцию
  • Путаница между async функциями и генераторами - это разные механизмы

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью