Как приостановить выполнение в Node.js: setTimeout, async/await и sleep-promise

Коротко о задаче
JavaScript не предоставляет встроенной функции sleep(), как некоторые другие языки. Вместо этого используются таймеры и промисы. Ниже — три рабочих подхода с примерами, плюс практические советы, когда что использовать.
Использование setTimeout для задержки
Функция setTimeout планирует выполнение кода через заданное число миллисекунд. Она принимает функцию и задержку в миллисекундах. Синтаксис:
setTimeout(function, timeInMs);Простой пример функции, которая печатает сообщение в консоль:
function printSentence() {
console.log("Using setTimeout()");
}
setTimeout(printSentence, 2000); // 2000 мс = 2 секундыВажно: setTimeout не блокирует поток выполнения. Это значит, что остальной код продолжит выполняться, и запланированная функция попадёт в очередь событий после истечения задержки и когда стек вызовов будет пуст.
Насколько точен setTimeout?
setTimeout обеспечивает минимум задержки, но не гарантию точного срабатывания в миллисекунду. Если поток занят другими синхронными операциями, выполнение отложенной функции может произойти позже запрошенного времени.
Пример поведения:
console.log("Hello World!");
function printSentence() {
console.log("Using setTimeout()");
}
setTimeout(printSentence, 2000);
console.log("Done");Вывод в консоль будет:
Hello World!
Done
Using setTimeout()Если нужен строго последовательный вывод с паузой между строками, setTimeout в чистом виде не даст блока выполнения.
Блокирующая «пауза» через busy-wait (не рекомендована для основного потока)
Можно симулировать блокирующую задержку циклом do…while, но это сильно грузит CPU и блокирует весь Node-процесс:
function delay(ms) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < ms);
}
console.log("Hello World");
delay(2000); // блокирует поток на 2 секунды
console.log("Will be printed after 2 seconds!");Используйте такой подход только в отдельных воркерах или для очень специфичных задач, где блокировка потока допустима.
Использование async/await для паузы
await приостанавливает выполнение асинхронной функции до тех пор, пока промис не перейдёт в состояние resolved. Это даёт чистый синтаксис и не блокирует весь поток выполнения.
Пример функции delay на промисе и её использование с async/await:
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
async function printSentence() {
console.log("Hello World");
await delay(2000); // пауза 2000 мс
console.log("Will be printed after 2 seconds");
}
printSentence();Вы получите последовательный вывод, при этом другие асинхронные операции в программе не будут заблокированы.
Плюсы async/await:
- Понятный, линейный контроль потока.
- Нет блокировки Event Loop.
- Хорошо сочетается с промисами и современным кодом.
Ограничение: await можно применять только внутри async-функции.
Пакет sleep-promise
sleep-promise — лёгкая библиотека, которая возвращает промис и упрощает код задержек.
Установка:
npm install sleep-promiseИспользование:
const sleep = require('sleep-promise');
(async () => {
console.log("Hello World.");
await sleep(2000); // 2 секунды
console.log("Will be printed after two seconds.");
})();Пакет полезен, если вы хотите минимизировать собственный код и сразу получить удобную функцию sleep.
Когда какой подход выбирать
- Нужна минимальная задержка без ожидания порядка выполнения — setTimeout.
- Нужен чистый последовательный код без глобальной блокировки — async/await с промисом (рекомендуется для большинства случаев).
- Хочется максимально компактного синтаксиса — sleep-promise.
- Нужна именно блокировка потока (редко) — используйте рабочие потоки (worker threads) или Atomics.wait в соответствующем контексте, но это выходит за рамки простых задержек.
Примеры, когда подходы дают сбой или не подходят
- Реaltime-система с жёсткими требованиями к задержке: JavaScript и Event Loop не гарантируют детерминированную миллисекундную точность.
- Встроенные циклы или тяжёлая синхронная работа в основном потоке отложит выполнение setTimeout.
- Busy-wait разрушает отзывчивость сервера и приводит к потере пропускной способности — не используйте в основном процессе.
Практическое руководство: чек-листы
Для разработчика:
- Определите, нужна ли блокировка потока.
- Если нет — используйте async/await или sleep-promise.
- Покройте задержки тестами, учитывая возможные задержки в окружении CI.
Для тестировщика:
- Тестируйте сценарии с тайм-аутами на нестабильной нагрузке.
- Убедитесь, что тесты не полагаются на точное время выполнения, а проверяют порядок событий.
Для DevOps:
- Избегайте busy-wait в контейнерах и на узлах с ограниченным CPU.
- Логируйте длительные задержки и пиковые нагрузки.
Критерии приёмки
- Код не блокирует основной поток без крайней необходимости.
- Поведение задержки покрыто тестами, допускающими небольшие вариации времени.
- В продакшне используется корректный инструмент: async/await или sleep-promise, если нужен читаемый код.
Быстрый справочник (ключевые числа и соответствия)
- 1000 мс = 1 с
- setTimeout(fn, 0) — планирует выполнение как можно скорее после освобождения стека
- Блокирующий delay через цикл — быстро расходует CPU
Простая методология выбора
- Нужна ли точная блокировка? Если да — используйте воркеры или низкоуровневые механизмы.
- Нужен порядок и читаемость — async/await.
- Нужна краткая запись без собственного промиса — sleep-promise.
- Только одно событие должно выполниться позже — setTimeout.
Визуальная подсказка (decision flow)
flowchart TD
A[Нужно приостановить код?] --> B{Требует блокировку потока?}
B -- Да --> C[Рассмотрите воркеры / Atomics.wait]
B -- Нет --> D{Нужна читабельность последовательного кода?}
D -- Да --> E[Используйте async/await + Promise]
D -- Нет --> F{Хотите минимальный код?}
F -- Да --> G[Используйте sleep-promise]
F -- Нет --> H[Используйте setTimeout]Краткое резюме
- setTimeout хорош для простых отложенных вызовов, но не блокирует порядок выполнения.
- async/await с промисом даёт удобную, неблокирующую паузу в последовательном коде.
- sleep-promise экономит время на написании обвязки и даёт чистый синтаксис.
Важно: не блокируйте Event Loop в основном процессе без крайней необходимости.
Заметки:
- Для высокоточных таймингов и realtime-задач JavaScript не подходит как основной инструмент.
- Для блокирующего поведения используйте воркеры или платформенные механизмы.
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента