Спим по-джедайски: полный гайд по sleep() в JavaScript 2025

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Вступление: почему у 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); // "Сон отменён"
    }
}

Блоги

Рекомендаций по книгам