SetTimeout: примеры (JAVASCRIPT)

Работа с задержкой выполнения в JavaScript: функция setTimeout
Раздел: Timer, Запуск
setTimeout(callback (function), delay (number), ...args (any)): number

Функция setTimeout

Функция setTimeout является частью Web API и позволяет отложить выполнение кода на указанное количество миллисекунд. Она часто используется для создания задержек, анимаций, троттлинга и дебаунсинга событий.

Когда используется:

  • Отложенный запуск функции.
  • Создание анимаций и интерактивных элементов.
  • Ограничение частоты вызова обработчиков событий (дебаунсинг и троттлинг).
  • Имитация асинхронного поведения.
  • Установка таймаутов для операций.

Аргументы:

  1. callback (функция или строка кода) — функция, которую нужно выполнить после задержки. Использование строки кода (например, "alert('Hi')") не рекомендуется из-за проблем с безопасностью и производительностью.
  2. delay (число, необязательный) — задержка в миллисекундах перед выполнением. По умолчанию 0. Реальная задержка может быть больше из-за Single-threaded nature Event Loop.
  3. arg1, arg2, ... (необязательные) — дополнительные аргументы, которые будут переданы в функцию обратного вызова.

Возвращаемое значение:

Функция возвращает числовой timeoutID — уникальный идентификатор таймера. Этот идентификатор можно использовать для отмены выполнения с помощью функции clearTimeout(timeoutID).

Основные примеры

Пример 1: Базовое использование

console.log('Начало');
setTimeout(() => {
    console.log('Сообщение после задержки');
}, 2000);
console.log('Конец');
Начало
Конек
Сообщение после задержки (через 2 секунды)

Пример 2: Передача аргументов в колбэк

function greet(name, punctuation) {
    console.log(`Привет, ${name}${punctuation}`);
}
setTimeout(greet, 1000, 'Анна', '!');
Привет, Анна! (через 1 секунду)

Пример 3: Отмена выполнения

const timeoutId = setTimeout(() => {
    console.log('Это сообщение не появится');
}, 3000);
clearTimeout(timeoutId);
console.log('Таймер отменен');
Таймер отменен

Похожие функции в JavaScript

setInterval

Функция setInterval вызывает переданную функцию неоднократно с заданным интервалом. Возвращает intervalID. Останавливается с помощью clearInterval.

let counter = 0;
const intervalId = setInterval(() => {
    console.log(`Тик ${++counter}`);
    if (counter >= 5) clearInterval(intervalId);
}, 1000);
Тик 1 (через 1 сек)
Тик 2 (через 2 сек)
...
Тик 5 (через 5 сек)

requestAnimationFrame

Оптимизированный метод для анимаций, синхронизированный с частотой обновления экрана. Предпочтительнее setTimeout для плавной анимации.

let start = Date.now();
function animate() {
    let timePassed = Date.now() - start;
    console.log(`Прошло времени: ${timePassed}мс`);
    if (timePassed < 2000) {
        requestAnimationFrame(animate);
    }
}
requestAnimationFrame(animate);

Выбор функции зависит от задачи: setTimeout для разовых задержек, setInterval для повторяющихся действий, requestAnimationFrame для анимаций.

Аналоги в других языках программирования

Python (time.sleep, threading.Timer)

В Python для паузы используется time.sleep, но он блокирует весь поток. Для асинхронного выполнения можно использовать threading.Timer или асинхронные конструкции asyncio.sleep.

import time
print("Начало")
time.sleep(2)
print("После задержки")
# Или асинхронно с asyncio:
# import asyncio
# async def main():
#     print("Начало")
#     await asyncio.sleep(2)
#     print("После задержки")
# asyncio.run(main())
Начало
(пауза 2 секунды)
После задержки

PHP (sleep, usleep)

Функции sleep (секунды) и usleep (микросекунды) также блокируют выполнение скрипта.

<?php
echo "Начало";
sleep(2); // Задержка в секундах
echo "После задержки";
?>
Начало (пауза 2 секунды) После задержки

C/C++ (Sleep)

В Windows API используется функция Sleep из windows.h (задержка в миллисекундах). В стандарте C++11 и выше можно использовать std::this_thread::sleep_for.

#include <iostream>
#include <chrono>
#include <thread>
int main() {
    std::cout << "Начало";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "После задержки";
    return 0;
}

В отличие от JavaScript, большинство аналогов являются синхронными и блокирующими.

Распространенные ошибки

Ошибка 1: Неправильный контекст `this`

При передаче метода объекта в setTimeout теряется контекст `this`.

const obj = {
    name: 'Объект',
    logName() {
        console.log(this.name);
    }
};
setTimeout(obj.logName, 100); // Выведет undefined
// Решение: использовать стрелочную функцию или bind
setTimeout(() => obj.logName(), 200);
setTimeout(obj.logName.bind(obj), 300);
undefined
Объект (через 200 мс)
Объект (через 300 мс)

Ошибка 2: Замыкание в цикле

Классическая проблема: все таймеры в цикле используют одно и то же значение переменной на момент выполнения.

for (var i = 1; i <= 3; i++) {
    setTimeout(() => console.log(i), i * 1000);
}
// Все выведут 4 (если var) или 3 (если let без замыкания)
4
4
4

Решение — создать новую область видимости для каждой итерации (использовать `let` с дополнительной функцией или IIFE).

for (let i = 1; i <= 3; i++) {
    setTimeout(() => console.log(i), i * 1000);
}
1
2
3

Ошибка 3: Ожидание точной задержки

setTimeout гарантирует минимальную, но не точную задержку из-за занятости основного потока.

const start = Date.now();
setTimeout(() => {
    console.log(`Прошло: ${Date.now() - start} мс`);
}, 100);
// Длительная операция блокирует Event Loop
for (let i = 0; i < 1e8; i++) {}
Прошло: 550 мс (значительно больше 100 мс)

Изменения в последних версиях

Спецификация функции setTimeout остается стабильной в рамках ECMAScript, так как она является частью Web API, а не ядра языка. Однако, среда выполнения (браузеры, Node.js) могут вносить изменения.

  • В современных браузерах минимальная задержка для вложенных таймеров (после 5 уровней вложенности) увеличивается до 4 мс (как указано в стандарте HTML5).
  • В неактивных вкладках браузеры могут замедлять выполнение setTimeout до 1 раза в секунду для оптимизации ресурсов.
  • Node.js имеет свои аналоги (setTimeout из модуля timers) с дополнительными возможностями, такими как ref() и unref().
  • Появились новые API, такие как queueMicrotask и scheduler.postTask, которые предлагают более точный контроль над очередями задач.

Расширенные примеры использования

Пример 1: Дебаунсинг (устранение дребезга)

Техника для ограничения частоты вызова функции, например, при обработке событий прокрутки или ввода.

Пример javascript
function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
}
const handleInput = debounce((value) => {
    console.log(`Отправка запроса с: ${value}`);
}, 500);
// Имитация быстрого ввода
handleInput('A');
handleInput('AB');
handleInput('ABC'); // Только этот вызов сработает через 500 мс
Отправка запроса с: ABC (через 500 мс после последнего вызова)

Пример 2: Троттлинг (ограничение частоты)

Гарантирует, что функция выполняется не чаще, чем раз в указанный период.

Пример javascript
function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}
const logScroll = throttle(() => console.log('Прокрутка!'), 1000);
// Многократный вызов в течение секунды сработает только один раз
logScroll(); // Сработает
logScroll(); // Проигнорировано
logScroll(); // Проигнорировано
// Через 1 секунду снова можно будет вызвать
Прокрутка!
(остальные вызовы в течение секунды игнорируются)

Пример 3: Создание цепочки таймеров (аналог последовательных задержек)

Позволяет выполнять код с паузами между этапами.

Пример javascript
function sequentialTimer(...actions) {
    let totalDelay = 0;
    actions.forEach(([callback, delay]) => {
        totalDelay += delay;
        setTimeout(callback, totalDelay);
    });
}
sequentialTimer(
    [() => console.log('Шаг 1'), 1000],
    [() => console.log('Шаг 2'), 2000],
    [() => console.log('Шаг 3'), 500]
);
Шаг 1 (через 1 сек)
Шаг 2 (через 3 сек от начала)
Шаг 3 (через 3.5 сек от начала)

Пример 4: Использование с промисами

Оборачивание setTimeout в промис для использования в асинхронном коде.

Пример javascript
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function delayedGreeting() {
    console.log('Ждем...');
    await delay(2000);
    console.log('Привет!');
}
delayedGreeting();
Ждем...
Привет! (через 2 секунды)

Пример 5: Измерение времени выполнения с таймаутом

Установка ограничения по времени для выполнения операции.

Пример javascript
function withTimeout(promise, ms) {
    const timeout = new Promise((_, reject) =>
        setTimeout(() => reject(new Error('Таймаут')), ms)
    );
    return Promise.race([promise, timeout]);
}
const slowOperation = new Promise(resolve => setTimeout(() => resolve('Данные'), 3000));
withTimeout(slowOperation, 2000)
    .then(data => console.log(data))
    .catch(err => console.log(err.message)); // Сработает таймаут
Таймаут (через 2 секунды, хотя операция занимает 3)

JS setTimeout function comments

En
SetTimeout Executes a function after a delay