Menu

Sleep в TypeScript: как сделать паузу или задержку

Встроенного sleep в TypeScript нет, но его легко написать в одну строку: функция возвращает Promise<void>, который разрешается через setTimeout. Разберём, как ждать её через await, делать паузы в циклах, повторять попытки с задержкой, отменять sleep и почему в JavaScript нет блокирующего sleep.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Встроенного sleep в TypeScript нет. Напишите функцию, которая возвращает Promise<void>, разрешаемый через setTimeout после задержки, и ждите её через await внутри функции async:

await sleep(300) приостанавливает только main. Программа не замирает: пока функция ждёт, таймеры, другие async-функции и ввод-вывод продолжают работать.

Как работает функция sleep

setTimeout(resolve, ms) планирует вызов resolve через ms миллисекунд, а вызов resolve завершает промис. await на этом промисе приостанавливает async-функцию до этого момента.

Аргумент типа <void> важен. Отдельно стоящий new Promise(...) не выводит свой тип из вызовов resolve, поэтому без него sleep возвращала бы Promise<unknown>. А void позволяет вызывать resolve без значения: в Promise<number> вызов resolve() без аргумента даёт ошибку компиляции. Если у функции объявлен тип возвращаемого значения, аргумент типа можно указать там:

setTimeout гарантирует минимальное, а не точное время: колбэк выполняется, когда задержка прошла и цикл событий свободен. Рассчитывайте на запас в несколько миллисекунд.

Пауза внутри цикла

await sleep() внутри цикла for или for...of делает паузу между итерациями. Так задают темп работы (ограничения частоты запросов, опрос, анимации в терминале):

Внутри forEach это не работает: он игнорирует промис, который возвращает каждый async-колбэк, поэтому все итерации запускаются сразу и ничего не ждёт. Используйте цикл for или for...of, как объясняется на странице async/await.

Повтор с задержкой

Частый реальный случай: попробовать выполнить операцию, а если она не удалась, подождать и попробовать снова, каждый раз удваивая ожидание (экспоненциальная задержка, exponential backoff).

return await fn() внутри try здесь важен: без await отклонённый промис был бы возвращён раньше, чем catch успел бы его увидеть.

Отменяемый sleep

Простой sleep нельзя прервать. Чтобы перестать ждать раньше (пользователь нажал «Отмена», началось завершение работы), передайте AbortSignal и очистите таймер, когда сигнал сработает:

Программа завершается сразу после отмены, потому что долгий таймер очищен. Без clearTimeout Node продолжал бы работать, пока не истекут 5 секунд.

Встроенные промис-таймеры Node

В Node есть промис-версия setTimeout в модуле node:timers/promises, поэтому в коде для Node её можно импортировать вместо того, чтобы писать свою функцию. Она также принимает значение, с которым разрешится промис, и AbortSignal:

import { setTimeout as sleep } from "node:timers/promises";

async function main() {
    await sleep(1000);                              // wait one second
    const result = await sleep(500, "done");        // resolves with "done"
    const ac = new AbortController();
    await sleep(10_000, undefined, { signal: ac.signal }); // cancellable
}

Типы берутся из @types/node (npm i -D @types/node). В браузерах этого модуля нет, и там переносимый вариант: вспомогательная функция в одну строку.

Блокирующего sleep не существует

В языках с потоками есть sleep, который блокирует текущий поток. JavaScript выполняет ваш код в одном потоке с циклом событий, поэтому блокирующий sleep заморозил бы всё остальное: таймеры не срабатывают, сетевые ответы не обрабатываются, а в браузере страница перестаёт отвечать. Цикл активного ожидания, который крутится, пока часы не дойдут до нужного времени, делает ровно это, и он никогда не бывает правильным решением.

Асинхронный sleep и есть способ ждать в JavaScript и TypeScript: функция приостанавливается, поток свободен, и функция продолжает работу, когда срабатывает таймер. Atomics.wait действительно умеет блокировать, но только на SharedArrayBuffer, и браузеры запрещают его в главном потоке; он существует для координации рабочих потоков, а не для задержек.

Часто задаваемые вопросы

Как сделать sleep в TypeScript?

Объявите const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); и вызовите await sleep(1000) внутри функции async. Эта функция приостановится примерно на секунду, а остальная программа продолжит работать.

Есть ли в TypeScript встроенная функция sleep?

Нет. TypeScript добавляет к JavaScript типы и не имеет собственной библиотеки времени выполнения, а в JavaScript нет sleep. В Node её можно импортировать: import { setTimeout as sleep } from "node:timers/promises". В браузере напишите вспомогательную функцию в одну строку.

Зачем sleep нужен await?

sleep(1000) сразу возвращает промис; только await приостанавливает окружающую async-функцию, пока промис не разрешится. Без await следующая строка выполнится немедленно. await работает только в функции async (или на верхнем уровне ES-модуля).

Можно ли заблокировать поток синхронным sleep в TypeScript?

В обычном коде нет. JavaScript работает в одном потоке с циклом событий, и цикл активного ожидания замораживает всё: таймеры, сетевые колбэки, всю страницу. Используйте асинхронный sleep. Atomics.wait умеет блокировать поток в Node или в воркере, но он нужен для низкоуровневой координации воркеров, а не для задержек.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ