Вступление: почему у JavaScript нет нормального sleep()?
Привет, коллеги-разработчики! Если вы когда-нибудь переходили на JavaScript с Python, PHP или Java, то наверняка задавались вопросом: «А где же тут мой любимый sleep(1), чтобы программа уснула на секунду?». И, порывшись в документации, с удивлением обнаруживали, что такой функции в чистом JS нет.
Это не баг, а фича. JavaScript — это однопоточный язык, работающий в цикле событий (Event Loop). Если бы мы просто заблокировали поток на секунду, то вместе с ним замер бы весь интерфейс, перестали бы обрабатываться клики, анимации и запросы. Блокирующий sleep() — это антипаттерн для веба.
Но это не значит, что мы не можем сделать паузу. Вопрос «What is the JavaScript version of sleep()?» на Stack Overflow собрал сотни тысяч просмотров, и в 2025 году у нас есть целый арсенал решений — от простых до изощренных.
В этой статье (специально для образовательного портала pythonlib.ru) мы разберем все легальные способы усыпить JavaScript, их плюсы и минусы, а также определим лучшие практики для разных ситуаций.
Самый правильный способ: async/await и промисы
Начиная с ES2017, в JavaScript появилась асинхронность на стероидах — async/await. Если вы хотите сделать паузу, не блокируя главный поток, вам нужна функция, возвращающая Promise, который разрешится через нужное время.
Вот она, наша кастомная sleep() для 2025 года:
// Универсальная функция задержки
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Использование
async function demo() {
console.log('Начинаем...');
await sleep(2000); // Ждём 2 секунды
console.log('Прошло 2 секунды!');
}
demo();
Это неблокирующий sleep. Пока выполняется await sleep(2000), остальной код (обработчики событий, другие асинхронные функции) продолжает работать. Event Loop не засыпает — он просто откладывает выполнение следующих строк до разрешения промиса.
Почему это лучшая практика?
- Не блокирует UI — интерфейс остаётся отзывчивым.
- Стандартный синтаксис — читается как обычный синхронный код.
- Подходит для Node.js — сервер не зависает.
- Поддерживается везде — современные браузеры и Node.js 8+.
Старый добрый setTimeout()
До эры async/await мы использовали setTimeout(). Он всё ещё жив и актуален, но требует коллбэков, что порождает «ад обратных вызовов».
console.log('Старт');
setTimeout(() => {
console.log('Прошло 1.5 секунды');
}, 1500);
console.log('Финиш (выполнится сразу)');
Вывод в консоль будет таким: Старт, затем Финиш (мгновенно), и только потом Прошло 1.5 секунды. Это не совсем sleep() в классическом понимании, но для простых сценариев — идеально.
Когда использовать setTimeout?
- Когда нужно выполнить действие один раз через некоторое время.
- В анимациях (например, запустить эффект через 300 мс).
- В простых скриптах без сложной логики.
Блокирующий sleep (и почему так делать нельзя)
Да, технически вы можете заморозить поток с помощью пустого цикла. Но это зло чистой воды.
function blockSleep(milliseconds) {
const start = Date.now();
while (Date.now() - start < milliseconds) {
// Пусто — процессор греется, вкладка виснет
}
}
console.log('Начало');
blockSleep(3000);
console.log('Прошло 3 секунды (но браузер завис)');
Этот код делает ровно то, что вы просите: останавливает выполнение на 3 секунды. Но вместе с ним останавливается всё: скролл, клики, анимации, другие скрипты. В Node.js такая штука заблокирует весь сервер.
Вердикт: Используйте блокирующий sleep только в учебных примерах, чтобы понять, как работает Event Loop. В боевом коде — никогда.
Асинхронный sleep с setInterval
Иногда нужно выполнять действие с задержкой между повторами. setInterval() — ваш друг, но будьте осторожны: он не ждёт завершения асинхронной операции.
let counter = 0;
const intervalId = setInterval(() => {
counter++;
console.log(`Тик ${counter}`);
if (counter >= 5) {
clearInterval(intervalId);
console.log('Стоп!');
}
}, 1000);
Этот код выводит «Тик 1», «Тик 2» … каждую секунду. Если вам нужно, чтобы интервал ждал завершения асинхронной задачи (например, fetch), лучше использовать рекурсивный setTimeout.
Современные тренды 2025 года
Мир не стоит на месте. Посмотрим, что появилось нового в мире пауз.
Timers API в Node.js (timers/promises)
В Node.js 16+ появился встроенный промис-таймер. Забудьте о кастомной функции sleep:
const { setTimeout } = require('timers/promises');
async function nodeSleep() {
console.log('Node.js ждёт...');
await setTimeout(3000);
console.log('Готово!');
}
Это нативный способ, не требующий обёрток. Для Node.js это лучшая практика 2025 года.
AbortController и отмена сна
Иногда нужно не только усыпить код, но и разбудить его досрочно. С помощью AbortController можно отменить ожидание:
function cancellableSleep(ms, signal) {
return new Promise((resolve, reject) => {
if (signal.aborted) {
return reject(new Error('Сон отменён'));
}
const timer = setTimeout(() => {
resolve();
}, ms);
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new Error('Сон отменён'));
}, { once: true });
});
}
// Использование
const controller = new AbortController();
setTimeout(() => controller.abort(), 1500); // Отменим через 1.5 сек
async function main() {
try {
await cancellableSleep(5000, controller.signal);
} catch (e) {
console.log(e.message); // "Сон отменён"
}
}
Блоги
Рекомендаций по книгам
Читайте также
Go: Как установить значения по умолчанию в структурах с максимальной производительностью
Паттерны проектирования в PHP на продвинутом уровне: примеры кода
C++ против ассемблера: почему компилятор обгоняет ручной код
BDD на Rust: Пишем живой код с Cucumber и Specs
Кэширование в Kotlin: Redis, Memcached и встроенные решения