• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

romejs.dev

  • Javascript
  • Blog

javascript settimeout richtig einsetzen: Praxis, Fallstricke, Event Loop und Best Practices

18. Oktober 2025 by Mike

Mit setTimeout planst du Code zur Ausführung in der Zukunft – einmalig und nach einer gewünschten Verzögerung in Millisekunden. In diesem Artikel bekommst du einen umfassenden, praxisnahen Leitfaden: von Syntax und Parametern über die Event Loop bis hin zu konkreten Patterns, Performance‑Tipps, Alternativen und Testbarkeit. Ziel ist, dass du javascript settimeout souverän einsetzt, Fehler vermeidest und wartbaren Code schreibst.


Grundlagen: Was setTimeout leistet und wie du es aufrufst

setTimeout(fn, delay, …args) ruft die Funktion fn einmalig nach mindestens delay Millisekunden auf. Zusätzliche Argumente werden an den Callback weitergereicht. Lässt du delay weg oder gibst 0 an, wird die Funktion so schnell wie möglich nach dem aktuell laufenden Stack ausgeführt (aber nie synchron).

// Basis
setTimeout(() => {
  console.log("Hallo, spätere Welt!");
}, 1000);

// Parameter weiterreichen
function greet(name) {
  console.log(`Hi, ${name}!`);
}
setTimeout(greet, 1500, "Alex"); // Hi, Alex! (nach 1,5 s)

Rückgabewert: Im Browser erhältst du eine numerische Timeout-ID. Mit clearTimeout(id) kannst du das geplante Event wieder stornieren. In Node.js ist der Rückgabewert ein Timeout-Objekt (mit .ref()/.unref()), das du an clearTimeout übergibst.

Wichtig: setTimeout blockiert nicht. Der restliche Code läuft weiter, während der Timer wartet.


Syntax, Parameter und Rückgabewerte im Detail

  • Callback: Funktionsreferenz oder Arrow Function. Übergib keinen direkten Funktionsaufruf.
  • Delay: Zahl in Millisekunden. Negative Werte, null oder NaN werden praktisch wie 0 behandelt.
  • Zusatzargumente: Optional. Werden dem Callback als Parameter gereicht.
  • Rückgabewert: Browser: Zahl (ID). Node: Timeout-Objekt.

Moderne JavaScript‑Funktionen, insbesondere seit ES6+, ermöglichen eine prägnante und lesbare Schreibweise.

// Häufiger Fehler: sofortiger Aufruf statt Referenz
setTimeout(doWork(), 1000); // falsch: doWork wird sofort ausgeführt
setTimeout(doWork, 1000);   // richtig: Funktionsreferenz

Nutze niemals die String‑Variante setTimeout("...") – sie ähnelt eval und ist u. a. aus Sicherheits‑ und Performancegründen nicht empfehlenswert.


javascript settimeout

clearTimeout: Geplante Aktionen sauber abbrechen

Halte die Rückgabe von setTimeout fest, wenn du die Ausführung ggf. stoppen willst:

let tid;

function plan() {
  tid = setTimeout(() => console.log("Geplant"), 2000);
}

function cancel() {
  clearTimeout(tid);
}

Das Abbrechen einer ungültigen oder bereits ausgelösten ID ist idempotent – es passiert einfach nichts. Diese Eigenschaft kannst du in robusten Cleanup‑Routinen nutzen.


Event Loop, Makro‑ und Mikro‑Tasks: Warum 0 ms nicht sofort ist

JavaScript ist Single‑Threaded (im Main‑Thread des Browsers). Die Event Loop arbeitet einen Call Stack ab und bedient danach Queues. Für das Timing wichtig:

  • Microtasks (Promises, queueMicrotask) haben Vorrang vor Makrotasks.
  • Makrotasks (z. B. setTimeout, setInterval, DOM‑Events) werden erst abgearbeitet, wenn der Stack leer ist und alle Microtasks erledigt sind.
setTimeout(() => console.log("timeout 0"), 0);

Promise.resolve().then(() => console.log("microtask"));

console.log("sync");

// Ausgabe:
// sync
// microtask
// timeout 0

Selbst bei delay 0 wird der Callback daher erst später ausgeführt – mindestens nach allen bereits geplanten Microtasks.


Genauigkeit, Clamping und Grenzen

  • Nesting‑Clamp: Browser setzen bei verschachtelten Timern einen Mindestwert (typisch 4 ms) ab einer gewissen Verschachtelungstiefe.
  • Maximale Verzögerung: Verzögerungen größer als 2_147_483_647 ms (ca. 24,8 Tage) führen zu Überläufen. Plane lange Wartezeiten in Segmenten.
  • Hintergrund‑Tab‑Throttling: Browser drosseln Timer in inaktiven Tabs aggressiv (z. B. bis auf 1 Aktivität pro Minute). Rechne nicht mit exakter Ausführung im Hintergrund.

Praxis‑Tipp: Benötigst du zuverlässigere Taktung unter Last, vermeide setInterval und nutze rekursives setTimeout oder verschiebe Arbeit in Web Worker.


javascript settimeout

setTimeout vs. setInterval vs. requestAnimationFrame

API Zweck Genauigkeit Ressourcen Typische Nutzung
setTimeout Einmalige Ausführung nach Delay OK, kann driften Gering Verzögerte Aktionen, Debounce, Backoff
setInterval Wiederholt nach Intervall Driftet bei Last Kontinuierlich Regelmäßige Polls, einfache Ticker (mit Vorsicht)
requestAnimationFrame Vor nächstem Repaint Framesynchron, sehr geeignet Nur im Frame Animationen, UI‑Rendering

Bei periodischen Aufgaben ist rekursives setTimeout oft robuster als setInterval:

let running = true;

function tick() {
  if (!running) return;
  const start = performance.now();
  doWork();
  const elapsed = performance.now() - start;
  const next = Math.max(0, 100 - elapsed); // kompensiere Arbeit
  setTimeout(tick, next);
}

setTimeout(tick, 100);

// später
running = false;

Praktische Muster mit setTimeout

1) Debounce: Eingaben entprellen

function debounce(fn, wait = 300) {
  let tid;
  return (...args) => {
    clearTimeout(tid);
    tid = setTimeout(() => fn(...args), wait);
  };
}

// Nutzung
const onInput = debounce((q) => search(q), 400);
inputEl.addEventListener("input", e => onInput(e.target.value));

2) Throttle mit Timeout

function throttle(fn, wait = 200) {
  let pending = false, lastArgs;
  return (...args) => {
    if (pending) { lastArgs = args; return; }
    pending = true;
    fn(...args);
    setTimeout(() => {
      pending = false;
      if (lastArgs) { const a = lastArgs; lastArgs = undefined; fn(...a); }
    }, wait);
  };
}

3) Sleep‑Funktion mit Promise und async/await

const sleep = (ms) => new Promise(res => setTimeout(res, ms));

async function run() {
  console.log("Start");
  await sleep(1000);
  console.log("1 Sekunde später");
}
run();

4) Zeitsensible UI: Toasts auto‑schließen, aber abbrechbar

function showToast(message, ms = 5000) {
  const el = document.createElement("div");
  el.className = "toast";
  el.textContent = message;
  document.body.appendChild(el);

  let tid = setTimeout(close, ms);

  function close() {
    clearTimeout(tid);
    el.remove();
  }

  el.addEventListener("mouseenter", () => clearTimeout(tid)); // Pausieren bei Hover
  el.addEventListener("mouseleave", () => (tid = setTimeout(close, ms)));
  el.addEventListener("click", close);
}

Häufige Fehler und wie du sie vermeidest

  • Callback statt Aufruf übergeben: setTimeout(fn, 1000) statt setTimeout(fn(), 1000).
  • this‑Kontext verlieren: Methoden verlieren beim direkten Übergeben ihr this. Lösung: Arrow‑Wrapper oder bind.
const obj = {
  v: 42,
  log() { console.log(this.v); }
};

setTimeout(obj.log, 10);                 // undefined (this verloren)
setTimeout(() => obj.log(), 10);         // 42 (Wrapper)
setTimeout(obj.log.bind(obj), 10);       // 42 (bind)
  • String‑Argument: Vermeiden. Niemals setTimeout("code") verwenden.
  • Vergessene Clearups: Beim Navigieren/Unmounten Timer immer abbrechen.
  • Große Delays: Für sehr große Wartezeiten segmentieren (oder persistente Scheduler nutzen).

Komponenten, Cleanup und Speicherlecks vermeiden

In Single‑Page‑Anwendungen ist sauberes Aufräumen essenziell. Beispiele:

React

useEffect(() => {
  const tid = setTimeout(() => setReady(true), 1000);
  return () => clearTimeout(tid); // Cleanup beim Unmount
}, []);

Vue

import { onMounted, onBeforeUnmount } from "vue";

let tid;
onMounted(() => { tid = setTimeout(() => state.ready = true, 1000); });
onBeforeUnmount(() => clearTimeout(tid));

Angular

export class Cmp implements OnDestroy {
  private tid?: number;
  ngOnInit() {
    this.tid = setTimeout(() => this.ready = true, 1000) as unknown as number;
  }
  ngOnDestroy() {
    if (this.tid) clearTimeout(this.tid);
  }
}

Diese Konzepte sind auch in modernen Frameworks entscheidend, um Ressourcen effizient zu verwalten.


Unterschiede und nützliche Features in der serverseitigen Umgebung

  • Rückgabewert: Timeout‑Objekt (nicht Zahl).
  • .unref()/.ref(): Mit unref verhindert man, dass ein Timer den Prozess wach hält.
  • Timers/Promises: timers/promises bietet Promise‑basierte setTimeout-Variante.
import { setTimeout as sleep } from "timers/promises";

await sleep(1000); // pausa, keine Wrapper nötig

const t = setTimeout(() => console.log("später"), 1000);
t.unref(); // Prozess kann beenden, auch wenn Timer noch läuft

Performance, Genauigkeit und Alternativen

  • UI‑Animationen: Nutze requestAnimationFrame oder CSS‑Transitions/Web Animations API statt setTimeout.
  • Arbeit entlasten: Längere Berechnungen in Web Worker auslagern. Timer dort werden nicht durch UI‑Rendering beeinflusst.
  • Messungen: Nutze performance.now() statt Date.now() für präzisere Laufzeitmessungen.
function loopWithRAF(timestampPrev) {
  requestAnimationFrame((ts) => {
    const dt = ts - (timestampPrev ?? ts);
    update(dt);
    render();
    loopWithRAF(ts);
  });
}
loopWithRAF();

Sicherheit: Keine Strings, keine Ausführung aus Daten

Ein expliziter Hinweis: Führe niemals von außen kommende Daten als Code aus. setTimeout("...") ist funktional wie eval – unsicher und schlecht für Performance. Nutze Funktionsreferenzen oder Arrow Functions.


Testbarkeit: Fake Timers in Jest und Co.

Unit‑Tests ohne echte Wartezeiten beschleunigst du mit Fake Timers:

jest.useFakeTimers();

test("ruft Callback nach 1s", () => {
  const cb = jest.fn();
  setTimeout(cb, 1000);

  jest.advanceTimersByTime(1000);
  expect(cb).toHaveBeenCalledTimes(1);
});

Ähnlich funktionieren Sinon oder Vitest. Achte bei Promises auf das Abarbeiten der Microtasks (await Promise.resolve() oder test‑spezifische Utilities).


Barrierefreiheit und UX: Zeitgesteuerte Aktionen bedacht einsetzen

  • Vermeide Zwang: Automatisch verschwindende Meldungen sollten pausierbar sein.
  • Screenreader: Nutze ARIA‑Live‑Regionen für dynamische Nachrichten.
  • Fokus: Verschiebe den Fokus nicht automatisch nach einem Timeout – das irritiert Nutzende.

Erweiterte Patterns: Abbruch, Kaskaden und Backoff

Abbrechbares Sleep

function sleepAbortable(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) return reject(signal.reason || new DOMException("Aborted", "AbortError"));
    const tid = setTimeout(resolve, ms);
    signal?.addEventListener("abort", () => {
      clearTimeout(tid);
      reject(signal.reason || new DOMException("Aborted", "AbortError"));
    }, { once: true });
  });
}

// Nutzung
const ctrl = new AbortController();
sleepAbortable(5000, ctrl.signal).catch(() => console.log("abgebrochen"));
ctrl.abort();

Exponential Backoff

async function fetchWithBackoff(url, { tries = 5, base = 300 } = {}) {
  for (let i = 0; i < tries; i++) {
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error(res.statusText);
      return res;
    } catch (e) {
      if (i === tries - 1) throw e;
      const wait = base * 2 ** i + Math.random() * 100;
      await new Promise(r => setTimeout(r, wait));
    }
  }
}

Debugging‑Tipps

  • DevTools: Aktiviere asynchrone Stacktraces, um den Ursprung von Timer‑Callbacks nachzuvollziehen.
  • Logs mit Labels: Logge Timer‑IDs und Timestamps, um Timing‑Probleme zu erkennen.
  • Last simulieren: Teste, wie sich Timer unter CPU‑Last verhalten (driftende Ausführung identifizieren).

Checkliste für den produktiven Einsatz

  • Benötigst du einmalig vs. periodisch? Wähle setTimeout vs. rekursives Timeout.
  • UI‑bezogene Taktung? Nutze requestAnimationFrame.
  • Cleanup sicherstellen: clearTimeout bei unmount/navigate.
  • Kein String‑Argument, keine versteckte eval.
  • Beachte Hintergrund‑Tab‑Throttling.
  • Bei langen Delays: Segmentieren oder alternative Scheduler.
  • Für Tests: Fake Timers einsetzen.

Vergleichstabelle: Browser vs. serverseitige Umgebung

Aspekt Browser serverseitige Umgebung
Rückgabewert ID (Zahl) Timeout‑Objekt
Abbrechen clearTimeout(id) clearTimeout(handle)
Zusatzfunktionen – .ref(), .unref()
Promise‑API Custom Wrapper timers/promises
Throttling Hintergrund‑Tab‑Drosselung Prozessgesteuert, kein Tab‑Konzept

Häufige Praxisfragen zu Zeitsteuerung

  • Countdown ohne Drift: Speichere Startzeit und berechne verbleibende Zeit anhand von performance.now(). Plane nächste Ausführung adaptiv.
  • Mehrere unabhängige Timer: Kein Problem; speichere IDs separat und räume konsistent auf.
  • Interaktionen pausieren Timer: Plane Pausier-/Fortsetzen‑Logik (z. B. beim Toast‑Beispiel).

Fazit

javascript settimeout ist simpel in der Anwendung, aber in Details komplex genug, um über UX, Performance und Korrektheit zu entscheiden. Verstehe die Event Loop, nutze clearTimeout konsequent, vermeide String‑Ausführung und wähle bewusst zwischen setTimeout, rekursivem Timeout, setInterval und requestAnimationFrame. In Komponenten sichert sauberes Cleanup die Stabilität, in der serverseitigen Umgebung helfen .unref() und die Promise‑Varianten. Mit diesen Best Practices baust du robuste zeitgesteuerte Abläufe, die auch unter Last und in Hintergrund‑Szenarien verlässlich funktionieren.


FAQ

Wie unterscheidet sich setTimeout von setInterval?
setTimeout führt einmalig nach einem Delay aus. setInterval wiederholt periodisch. Für präzisere Periodik ist rekursives setTimeout oft besser, da es Drift reduziert und erst nach Abschluss der Arbeit neu plant.

Ist setTimeout genau?
Nicht millisekundengenau. UI‑Last, Hintergrund‑Tab‑Throttling und Clamping beeinflussen die tatsächliche Ausführungszeit. Für Framesynchronität nutze requestAnimationFrame, für stabile Periodik adaptives Re‑Scheduling.

Warum wird mein setTimeout mit 0 ms nicht sofort ausgeführt?
Weil setTimeout eine Makrotask plant. Erst nach leeren Call Stack und allen Microtasks (z. B. Promise‑Callbacks) wird sie ausgeführt.

Wie breche ich einen geplanten Timeout ab?
Speichere die Rückgabe von setTimeout und rufe clearTimeout darauf auf. In Komponenten‑Frameworks: im Cleanup/Unmount‑Hook abbrechen.

Kann ich setTimeout sicher mit async/await nutzen?
Ja, wickle setTimeout in eine Promise. In der serverseitigen Umgebung gibt es timers/promises für eine native Promise‑Variante.

Warum geht this in Methoden verloren?
Beim Direktübergeben einer Methode wird der Kontext getrennt. Nutze .bind(obj) oder einen Arrow‑Wrapper, der obj.method() aufruft.

Gibt es ein Maximum für Delay?
Ja. Praktisch sind Verzögerungen größer als ~24,8 Tage problematisch (32‑Bit‑Ganzzahl). Segmentiere lange Wartezeiten in kürzere Intervalle.

Ist setTimeout in Hintergrund‑Tabs zuverlässig?
Nein, Browser drosseln Timer massiv in Hintergrund‑Tabs, um Energie zu sparen. Plane damit, dass Ausführungen sich verzögern.

Was ist besser für Animationen?
requestAnimationFrame. Es synchronisiert sich mit dem Repaint‑Zyklus und ist effizienter und flüssiger als setTimeout.

Wie teste ich Timer ohne echte Wartezeit?
Mit Fake Timers (z. B. Jest: useFakeTimers, advanceTimersByTime). So simulierst du Zeitfortschritt deterministisch.


Weiterführende Quellen

  • MDN: setTimeout
  • MDN: clearTimeout
  • MDN: setInterval
  • MDN: requestAnimationFrame
  • WHATWG HTML Standard: Timers
  • Serverseitige Umgebung Doku: Timers
  • Serverseitige Umgebung Doku: timers/promises
  • Chrome Developers: Timer Throttling
  • MDN: performance.now()
  • MDN: Web Workers
  • Philip Roberts – What the heck is the event loop anyway?
  • MDN: Microtask Guide

Filed Under: Javascript

Primary Sidebar

Neue Beiträge

  • Die besten ChatGPT-Alternativen 2025: Welche Lösung passt wirklich zu deinem Workflow?
  • OK Google, mein Gerät einrichten Android TV: Dein kompletter Praxis‑Leitfaden
  • KI programmieren für Anfänger: Dein pragmatischer Start in Python, ML und Deep Learning
  • Node.js in der Praxis: Architektur, Tools, Best Practices und Fallstricke
  • javascript settimeout richtig einsetzen: Praxis, Fallstricke, Event Loop und Best Practices

Copyright © 2025 · romejs.dev