JS Tower
JavaScript & TypeScriptМодуль 5: Асинхронность

Таймеры

setTimeout, setInterval и requestAnimationFrame

Цель урока

В этом уроке ты научишься:

  • Откладывать выполнение кода
  • Создавать повторяющиеся действия
  • Отменять таймеры

setTimeout

Выполняет функцию один раз через указанное время:

// setTimeout(callback, delay, ...args)

setTimeout(() => {
  console.log("Прошло 2 секунды");
}, 2000);

// С аргументами
setTimeout((name, age) => {
  console.log(`${name}, ${age} лет`);
}, 1000, "Иван", 25);

Отмена таймера

let timerId = setTimeout(() => {
  console.log("Это не выполнится");
}, 5000);

// Отменяем до выполнения
clearTimeout(timerId);

setInterval

Выполняет функцию регулярно через указанный интервал:

let count = 0;

let intervalId = setInterval(() => {
  count++;
  console.log("Счётчик: " + count);
  
  if (count >= 5) {
    clearInterval(intervalId);
    console.log("Остановлено");
  }
}, 1000);

Проблема накопления

// Если функция выполняется дольше интервала,
// вызовы накапливаются

setInterval(() => {
  // Тяжёлая операция на 2 секунды
  // при интервале 1 секунда
}, 1000);

Решение — вложенный setTimeout

let count = 0;

function tick() {
  count++;
  console.log("Счётчик: " + count);
  
  if (count < 5) {
    setTimeout(tick, 1000);
  }
}

setTimeout(tick, 1000);

Минимальная задержка

// Браузер гарантирует минимум ~4мс для вложенных таймеров
setTimeout(() => {
  setTimeout(() => {
    setTimeout(() => {
      setTimeout(() => {
        setTimeout(() => {
          // Здесь задержка уже ~4мс минимум
        }, 0);
      }, 0);
    }, 0);
  }, 0);
}, 0);

Нулевая задержка

setTimeout(fn, 0) не означает "немедленно". Callback попадает в очередь макрозадач.


requestAnimationFrame

Для анимаций — синхронизируется с обновлением экрана (~60 FPS):

let position = 0;

function animate() {
  position += 2;
  element.style.left = position + "px";
  
  if (position < 300) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

Отмена

let animationId = requestAnimationFrame(animate);
cancelAnimationFrame(animationId);

Преимущества

  • Автоматическая пауза при скрытии вкладки
  • Оптимальная частота обновления
  • Плавная анимация

Практические примеры

Debounce

function debounce(fn, delay) {
  let timerId;
  
  return function(...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn.apply(this, args), delay);
  };
}

// Использование
const search = debounce((query) => {
  console.log("Поиск: " + query);
}, 300);

search("a");
search("ab");
search("abc"); // Только этот вызов выполнится

Throttle

function throttle(fn, delay) {
  let lastTime = 0;
  
  return function(...args) {
    let now = Date.now();
    if (now - lastTime >= delay) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

// Использование
const handleScroll = throttle(() => {
  console.log("Скролл");
}, 100);

Таймер обратного отсчёта

function countdown(seconds) {
  let remaining = seconds;
  
  let intervalId = setInterval(() => {
    console.log(remaining);
    remaining--;
    
    if (remaining < 0) {
      clearInterval(intervalId);
      console.log("Время вышло!");
    }
  }, 1000);
}

countdown(5);

Сравнение

МетодНазначениеПовторение
setTimeoutОтложенное выполнениеОдин раз
setIntervalРегулярное выполнениеМногократно
requestAnimationFrameАнимацияКаждый кадр

Практика

Задание 1: Отложенное сообщение

Задача: Выведи сообщение через 2 секунды.

Loading...
Ваш вывод:

Задание 2: Счётчик

Задача: Создай счётчик от 1 до 5 с интервалом 1 секунда.

Loading...
Ваш вывод:

Задание 3: Debounce

Задача: Реализуй простой debounce.

Loading...
Ваш вывод:

Проверь себя

  1. Чем setTimeout отличается от setInterval?
  2. Как отменить таймер?
  3. Когда использовать requestAnimationFrame?