11. března 2026

Časování: setTimeout a setInterval

Můžeme se rozhodnout, že funkci nespustíme právě teď, ale až za nějakou dobu. To se nazývá „časování volání“ („scheduling a call“).

K tomu existují dvě metody:

  • setTimeout nám umožňuje spustit funkci jednou po uplynutí zadaného časového intervalu.
  • setInterval nám umožňuje spouštět funkci opakovaně, nejprve po uplynutí zadaného časového intervalu a pak bude neustále v tomto intervalu opakována.

Tyto metody nejsou součástí specifikace JavaScriptu, ale většina prostředí obsahuje interní plánovač a tyto metody poskytuje. Konkrétně jsou podporovány ve všech prohlížečích a v Node.js.

setTimeout

Syntaxe:

let idČasovače = setTimeout(funkce|kód, [prodleva], [arg1], [arg2], ...)

Parametry:

funkce|kód
Funkce nebo řetězec kódu, který se má spustit. Obvykle to bývá funkce. Z historických důvodů lze předat i řetězec kódu, ale to se nedoporučuje.
prodleva
Prodleva před spuštěním v milisekundách (1000 ms = 1 sekunda), standardně 0.
arg1, arg2…
Argumenty této funkce.

Například tento kód zavolá řekniAhoj() po uplynutí jedné sekundy:

function řekniAhoj() {
  alert('Ahoj');
}

setTimeout(řekniAhoj, 1000);

S argumenty:

function řekniAhoj(věta, kdo) {
  alert( věta + ', ' + kdo );
}

setTimeout(řekniAhoj, 1000, "Ahoj", "Jan"); // Ahoj, Jan

Je-li první argument řetězec, pak z něj JavaScript vytvoří funkci.

Bude tedy fungovat i tohle:

setTimeout("alert('Ahoj')", 1000);

Používání řetězců se však nedoporučuje, místo nich používejte šipkové funkce, například takto:

setTimeout(() => alert('Ahoj'), 1000);
Předejte funkci, ale nevolejte ji

Začínající vývojáři někdy dělají chybu v tom, že za funkci přidají závorky ():

// špatně!
setTimeout(řekniAhoj(), 1000);

To nefunguje, protože setTimeout očekává odkaz na funkci. A zde řekniAhoj() zavolá funkci a teprve výsledek jejího volání se předá funkci setTimeout. V našem případě výsledek řekniAhoj() je undefined (funkce nic nevrací), takže se nic nenačasuje.

Zrušení pomocí clearTimeout

Volání setTimeout vrátí „identifikátor časovače“ idČasovače, který můžeme použít ke zrušení načasovaného spuštění.

Syntaxe zrušení:

let idČasovače = setTimeout(...);
clearTimeout(idČasovače);

V následujícím kódu načasujeme funkci a pak ji zrušíme (rozmysleli jsme si to). Výsledkem bude, že se nic nestane:

let idČasovače = setTimeout(() => alert("tohle se nikdy nestane"), 1000);
alert(idČasovače); // identifikátor časovače

clearTimeout(idČasovače);
alert(idČasovače); // stejný identifikátor (po zrušení se nevynuloval)

Jak vidíme z výstupu alert, v prohlížeči je identifikátorem časovače číslo. V jiných prostředích to může být něco jiného, například Node.js vrací objekt časovače s dalšími metodami.

Opakujeme, že pro tyto metody neexistuje žádná univerzální specifikace, takže je to v pořádku.

Pro prohlížeče jsou časovače popsány v kapitole o časovačích HTML Living Standardu.

setInterval

Metoda setInterval má stejnou syntaxi jako setTimeout:

let idČasovače = setInterval(funkce|kód, [prodleva], [arg1], [arg2], ...)

Všechny argumenty mají stejný význam. Na rozdíl od setTimeout však funkce nebude spuštěna jenom jednou, ale pravidelně vždy po uplynutí zadaného časového intervalu.

Chceme-li zastavit další volání, měli bychom zavolat clearInterval(idČasovače).

Následující příklad zobrazí zprávu každé 2 sekundy. Po 5 sekundách bude výstup zastaven:

// opakovat s intervalem 2 sekundy
let idČasovače = setInterval(() => alert('tik'), 2000);

// konec po 5 sekundách
setTimeout(() => { clearInterval(idČasovače); alert('stop'); }, 5000);
Zatímco je zobrazen alert, čas plyne dál

Ve většině prohlížečů včetně Chrome a Firefoxu v době, kdy je zobrazeno alert/confirm/prompt, vnitřní časovač „tiká“ dál.

Když si tedy spustíte výše uvedený kód a okno alert nějakou dobu nezrušíte, pak bude následující alert zobrazen hned, jakmile to uděláte. Skutečný interval mezi zobrazeními tedy bude kratší než 2 sekundy.

Vnořený setTimeout

Existují dva způsoby, jak něco pravidelně spouštět.

První je setInterval. Druhý je vnořený setTimeout, například takto:

/** namísto:
let idČasovače = setInterval(() => alert('tik'), 2000);
*/

let idČasovače = setTimeout(function tik() {
  alert('tik');
  idČasovače = setTimeout(tik, 2000); // (*)
}, 2000);

Uvedený setTimeout načasuje další volání na konci aktuálního na řádku (*).

Vnořený setTimeout je flexibilnější metoda než setInterval. Tímto způsobem můžeme příští volání načasovat za jinou dobu, podle výsledků aktuálního volání.

Například potřebujeme napsat službu, která každých 5 sekund pošle na server žádost o data, ale v případě, že je server přetížen, by tento interval měla zvýšit na 10, 20, 40 sekund…

Zde je pseudokód:

let prodleva = 5000;

let idČasovače = setTimeout(function požadavek() {
  ...pošle požadavek...

  if (požadavek neuspěl kvůli přetížení serveru) {
    // zvýšíme interval dalšího spuštění
    prodleva *= 2;
  }

  idČasovače = setTimeout(požadavek, prodleva);

}, prodleva);

A jestliže funkce, které časujeme, zatěžují CPU, můžeme měřit dobu, jakou trvá jejich spuštění, a naplánovat si další volání na dříve nebo později.

Vnořený setTimeout nám umožňuje nastavovat prodlevu mezi voláními přesněji než setInterval.

Porovnejme si dva fragmenty kódu. První používá setInterval:

let i = 1;
setInterval(function() {
  funkce(i++);
}, 100);

Druhý používá vnořený setTimeout:

let i = 1;
setTimeout(function spusť() {
  funkce(i++);
  setTimeout(spusť, 100);
}, 100);

Pro setInterval vnitřní plánovač spustí funkce(i++) každých 100 ms:

Všimli jste si?

Skutečná prodleva mezi voláními funkce pro setInterval je nižší, než uvedená v kódu!

To je normální, protože doba, kterou zabere výkon funkce, „spotřebuje“ část intervalu.

Může se stát, že výkon funkce bude trvat déle, než jsme očekávali, a vyžádá si více než 100 ms.

V takovém případě motor počká, až se funkce dokončí, pak zkontroluje plánovač, a pokud jeho čas vypršel, spustí funkci okamžitě znovu.

V krajním případě, jestliže výkon funkce trvá vždy déle než prodleva ms, bude k voláním docházet úplně bez prodlevy.

A zde je obrázek pro vnořený setTimeout:

Vnořený setTimeout zaručuje pevnou prodlevu (zde 100 ms) před koncem jednoho volání a začátkem následujícího.

Je to proto, že nové volání je naplánováno na konci předchozího.

Sběr odpadků a callback funkce setInterval/setTimeout

Když je do setInterval/setTimeout předána funkce, vytvoří se na ni interní odkaz a ten se uloží do plánovače. To brání sběrači odpadků, aby funkci odstranil, i když na ni nejsou žádné jiné odkazy.

// funkce zůstane v paměti, dokud ji plánovač nezavolá
setTimeout(function() {...}, 100);

Pro setInterval funkce zůstane v paměti, dokud není zavolán clearInterval.

To má vedlejší efekt. Funkce se odkazuje na vnější lexikální prostředí, takže dokud existuje, existují i vnější proměnné. Ty mohou zabrat mnohem více paměti než samotná funkce. Když tedy načasovanou funkci už nepotřebujeme, je lepší ji zrušit, i kdyby byla velmi malá.

setTimeout s nulovou prodlevou

Existuje speciální způsob použití: setTimeout(funkce, 0) nebo jen setTimeout(funkce).

To načasuje výkon funkce na co nejdříve. Plánovač ji však spustí až poté, co bude dokončen aktuálně běžící skript.

Funkce je tedy načasována ke spuštění „hned po“ aktuálním skriptu.

Například tohle vypíše „Ahoj“ a hned pak „světe“:

setTimeout(() => alert("světe"));

alert("Ahoj");

První řádek „uloží volání do kalendáře za 0 ms“. Avšak plánovač „zkontroluje kalendář“ až poté, co bude aktuální skript dokončen, takže "Ahoj" půjde jako první a "světe" až po něm.

V prohlížečích existují i pokročilejší způsoby použití načasování s nulovou prodlevou, které probereme v kapitole Smyčka událostí: mikroúlohy a makroúlohy.

Nulová prodleva není ve skutečnosti nulová (v prohlížeči)

V prohlížeči je omezení, jak často se mohou vnořené časovače spouštět. HTML Living Standard říká: „po pěti vnořených časovačích musí být interval alespoň 4 milisekundy.“

Na následujícím příkladu si předvedeme, co to znamená. Volání setTimeout v něm načasuje samo sebe s nulovou prodlevou. Každé volání si pamatuje v poli časy skutečný čas, který uplynul od předchozího. Jak budou vypadat skutečné prodlevy? Podívejme se:

let začátek = Date.now();
let časy = [];

setTimeout(function spusť() {
  časy.push(Date.now() - začátek); // zapamatujeme si prodlevu od předchozího volání

  if (začátek + 100 < Date.now()) alert(časy); // za 100 ms zobrazíme prodlevy
  else setTimeout(spusť); // jinak znovu načasujeme
});

// příklad výstupu:
// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100

Nejprve se časovače spustí okamžitě (tak, jak je uvedeno ve specifikaci) a pak vidíme 9, 15, 20, 24.... Povinná prodleva aspoň 4 ms mezi voláními vstupuje do hry.

Podobná věc se stane, jestliže použijeme setInterval namísto setTimeout: setInterval(f) spustí f několikrát s nulovou prodlevou a poté s prodlevou nejméně 4 ms.

Toto omezení pochází ze starých časů a mnoho skriptů se na ně spoléhá, takže existuje z historických důvodů.

Pro JavaScript na straně serveru toto omezení neexistuje a jsou tam jiné způsoby, jak načasovat okamžitou asynchronní činnost, například setImmediate pro Node.js. Tato poznámka je tedy specifická pro prohlížeče.

Shrnutí

  • Metody setTimeout(funkce, prodleva, ...argumenty) a setInterval(funkce, prodleva, ...argumenty) nám umožňují spustit funkci funkce jednou/pravidelně po prodleva milisekundách.
  • Chceme-li spuštění zrušit, měli bychom volat clearTimeout/clearInterval s hodnotou, kterou vrátila funkce setTimeout/setInterval.
  • Flexibilnější alternativou k setInterval jsou vnořená volání setTimeout, která nám umožňují nastavit čas mezi voláními přesněji.
  • Nulová prodleva v setTimeout(funkce, 0) (totéž jako setTimeout(funkce)) se používá k načasování volání „co nejdříve, ale až po dokončení aktuálního skriptu“.
  • Prohlížeč omezuje minimální prodlevu pro pět nebo více vnořených volání setTimeout nebo pro setInterval (po 5. volání) na 4 ms. Je tomu tak z historických důvodů.

Prosíme všimněte si, že žádná časovací metoda nezaručuje přesnou prodlevu.

Například časovač v prohlížeči může zpomalit z mnoha důvodů:

  • CPU je přetížen.
  • Záložka prohlížeče běží na pozadí.
  • Laptop je v režimu úspory baterie.

To všechno může zvýšit minimální rozlišení časovače (minimální prodlevu) na 300 ms nebo dokonce 1000 ms v závislosti na prohlížeči a nastavení výkonu na úrovni OS.

Úlohy

důležitost: 5

Napište funkci vypišČísla(začátek, konec), která každou sekundu vypíše číslo, přičemž začne číslem začátek a skončí číslem konec.

Vytvořte dvě varianty řešení.

  1. Pomocí setInterval.
  2. Pomocí vnořeného setTimeout.

Pomocí setInterval:

function vypišČísla(začátek, konec) {
  let aktuální = začátek;

  let idČasovače = setInterval(function() {
    alert(aktuální);
    if (aktuální == konec) {
      clearInterval(idČasovače);
    }
    aktuální++;
  }, 1000);
}

// použití:
vypišČísla(5, 10);

Pomocí vnořeného setTimeout:

function vypišČísla(začátek, konec) {
  let aktuální = začátek;

  setTimeout(function spusť() {
    alert(aktuální);
    if (aktuální < konec) {
      setTimeout(spusť, 1000);
    }
    aktuální++;
  }, 1000);
}

// použití:
vypišČísla(5, 10);

Všimněte si, že v obou řešeních je úvodní prodleva před prvním výstupem. Funkce je poprvé volána za 1000 ms.

Jestliže chceme, aby se funkce spustila okamžitě, můžeme přidat další volání na samostatný řádek, například takto:

function vypišČísla(začátek, konec) {
  let aktuální = začátek;

  function spusť() {
    alert(aktuální);
    if (aktuální == konec) {
      clearInterval(idČasovače);
    }
    aktuální++;
  }

  spusť();
  let idČasovače = setInterval(spusť, 1000);
}

vypišČísla(5, 10);
důležitost: 5

V následujícím kódu je načasováno volání setTimeout, pak proběhne náročný výpočet, jehož dokončení bude trvat více než 100 ms.

Kdy se načasovaná funkce spustí?

  1. Po cyklu.
  2. Před cyklem.
  3. Na začátku cyklu.

Co zobrazí alert?

let i = 0;

setTimeout(() => alert(i), 100); // ?

// předpokládáme, že doba výkonu této funkce je větší než 100 ms
for(let j = 0; j < 100000000; j++) {
  i++;
}

Každý setTimeout se spustí teprve po dokončení aktuálního kódu.

Proměnná i bude obsahovat poslední hodnotu: 100000000.

let i = 0;

setTimeout(() => alert(i), 100); // 100000000

// předpokládáme, že doba běhu této funkce je větší než 100 ms
for(let j = 0; j < 100000000; j++) {
  i++;
}
Mapa tutoriálu

Komentáře

přečtěte si před komentováním…
  • Máte-li návrhy na zlepšení, vytvořte prosím issue na GitHubu nebo pull request místo komentáře.
  • Pokud v článku něčemu nerozumíte, napište prosím, čemu přesně a na kterém místě.
  • Pro vložení několika slov kódu použijte značku <code>, pro několik řádků je obalte značkou <pre>, pro více než 10 řádků vložte odkaz na pískoviště (plnkr, jsbin, codepen…)