Как реализовать асинхронную функцию sleep, которая ждет заданное количество миллисекунд и успешно завершается?
Что проверяют
Понимаете ли вы, как промис связан с таймером и циклом событий, и что await sleep() приостанавливает только текущую асинхронную функцию, а не весь поток. Следом обычно просят добавить отмену или объяснить, почему «ровно 1000 мс» не бывает.
Как отвечать
Базовая версия в одну строку: const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));. Функция resolve передаётся в setTimeout без вызова и срабатывает по таймеру, промис выполняется со значением undefined.
Дальше покажите, что понимаете детали:
- точность:
setTimeoutгарантирует «не раньше чем», а не «ровно». Колбэк встаёт в очередь макрозадач и ждёт, пока освободится стек. Вложенные таймеры глубже пяти уровней браузер поднимает минимум до 4 мс, а в фоновых вкладках таймеры троттлятся до секунды и больше; - предел: задержка больше
2 ** 31 - 1мс (около 24,8 суток) переполняется, и таймер срабатывает почти сразу; - отмена: вторым параметром принимайте
AbortSignal. Если сигнал уже отменён, сразуreject(signal.reason), иначе подписываетесь на событиеabortс{ once: true }, в обработчике делаетеclearTimeoutи отклоняете промис. Так ожидание прерывается при размонтировании компонента; - готовое: в Node.js есть
setTimeoutизnode:timers/promises, он сразу возвращает промис и понимаетsignal.
Упомяните, где это нужно: пауза между повторами запроса с экспоненциальной задержкой, имитация сети в прототипе. В тестах вместо реального ожидания берут фейковые таймеры, vi.useFakeTimers() или jest.useFakeTimers(), чтобы тест не ждал настоящие секунды.
Где спотыкаются
Пишут setTimeout(resolve(), ms). Скобки вызывают resolve сразу, и промис выполняется без всякой задержки, а таймер получает undefined вместо функции.
Реализуют задержку циклом while (Date.now() < end) {}. Это блокирует главный поток: интерфейс замирает, клики и отрисовка ждут вместе с циклом.
Ставят await sleep() внутри forEach и удивляются, что паузы не работают. forEach не ждёт промисы, которые возвращает колбэк, все итерации стартуют разом. Для последовательных пауз нужен for...of.