setTimeout и setInterval #
setTimeout откладывает однократный вызов функции; setInterval повторяет её через заданный интервал. Оба планируют колбэк в очереди задач браузера или Node.js — код после вызова таймера выполняется сразу, не дожидаясь колбэка.
setTimeout #
Синтаксис: setTimeout(callback, delayMs, ...args). Возвращает числовой id для отмены:
function showHint(message) {
console.log(message)
}
const timerId = setTimeout(showHint, 1500, 'Продолжайте урок — вы на верном пути')
// отмена до срабатывания
clearTimeout(timerId)
delay — минимальная задержка, не гарантия точного времени: при загруженной вкладке или длинной синхронной задаче колбэк сработает позже.
console.log('старт')
setTimeout(() => console.log('таймер'), 0)
console.log('конец')
// старт → конец → таймер
Даже 0 мс ставит задачу в очередь после текущего синхронного кода.
setInterval #
Повторяет колбэк, пока не вызовут clearInterval:
let seconds = 0
const tickId = setInterval(() => {
seconds += 1
console.log(`Прошло ${seconds} с`)
if (seconds >= 3) {
clearInterval(tickId)
}
}, 1000)
Для точного ритма «раз в N мс после завершения предыдущего шага» чаще используют цепочку setTimeout, а не setInterval — интервал не ждёт, пока колбэк закончит работу.
function pollLessonStatus(lessonId) {
function step() {
// запрос к API...
setTimeout(step, 5000)
}
step()
}
Аргументы колбэка #
Дополнительные параметры после задержки передаются в функцию:
setTimeout(
(title, step) => {
console.log(`${title}: шаг ${step}`)
},
300,
'JSFlow',
2
)
Стрелки и обычные функции ведут себя одинаково; для методов объекта следите за this — при необходимости используйте bind (см. отдельный урок).
Event loop в двух словах #
JavaScript в одном потоке выполняет синхронный код до конца. Асинхронные источники (таймеры, сеть, события DOM) ставят колбэки в очереди; движок забирает их, когда стек вызовов пуст.
function loadChapter() {
console.log('1: запросили данные')
setTimeout(() => console.log('3: таймер сработал'), 100)
fetch('/api/chapter/5')
.then(() => console.log('4: ответ сети'))
.catch(() => {})
console.log('2: UI не блокируется')
}
Порядок «3» и «4» зависит от того, что завершится раньше — сеть или таймер. Синхронные 1 и 2 всегда идут первыми.
Таймеры — не «пауза в языке», а планирование задачи на потом. Долгие циклы и тяжёлые вычисления всё равно блокируют интерфейс.
Практика: debounce поиска #
Типичный приём — откладывать реакцию на ввод, пока пользователь не перестанет печатать:
function createDebouncedSearch(onSearch, waitMs = 400) {
let timerId = null
return function handleInput(query) {
clearTimeout(timerId)
timerId = setTimeout(() => onSearch(query), waitMs)
}
}
const search = createDebouncedSearch((q) => {
console.log('ищем:', q)
}, 300)
search('ф')
search('фу')
search('функции') // один вызов onSearch с "функции"
Здесь и таймер, и замыкание на timerId.
Частые ошибки #
- Забыли
clearInterval— утечка и лишняя нагрузка после ухода со страницы. thisв методе объекта — передать колбэк без привязки:setTimeout(obj.tick, 1000)потеряет контекст.- Ожидание точных 1000 мс — вкладка в фоне, троттлинг браузера и длинные задачи сдвигают срабатывание.
- Рекурсивный
setIntervalбез условия выхода — бесконечный цикл; всегда предусматривайте остановку.
Итог #
setTimeout— один отложенный вызов;setInterval— повторяющийся, пока не отменят.- Колбэк таймера выполняется асинхронно, после текущего синхронного кода.
- Таймеры участвуют в event loop: задачи ждут освобождения стека.
- Для опроса API и поиска по вводу часто комбинируют таймеры с замыканиями и
clearTimeout.