Start · Sprachen · JavaScript · Referenz · Scheduling.isInputPending()

Scheduling.isInputPending()

Methode

Prüft, ob in der Ereignis-Warteschlange Eingabeereignisse (Klicks, Tasten usw.) warten. So kann lang laufender Code die Kontrolle rechtzeitig an den Browser abgeben.

seit JavaScript Experimentell: Chrome/Edge/Opera 87+, nicht in Fir Kategorie: webapi

Signatur

Scheduling.prototype.isInputPending(options)

Beschreibung

Die Methode isInputPending() wird über navigator.scheduling aufgerufen. Sie gibt true zurück, wenn der Benutzer mit der Seite interagiert, diese Eingabe aber noch nicht verarbeitet wurde. Das ist zum Beispiel der Fall, wenn gerade eine lange Aufgabe auf dem Hauptthread läuft. So muss man lange Rechenschleifen nicht in festen Abständen „auf Verdacht“ unterbrechen. Man gibt nur dann an die Ereignisschleife ab, wenn tatsächlich eine Eingabe wartet.

Das typische Muster sieht so aus: Man arbeitet eine Aufgabenliste in einer Schleife ab und prüft zwischendurch isInputPending(). Liefert die Methode true, unterbricht man die Arbeit, zum Beispiel mit setTimeout, scheduler.yield() oder MessageChannel. Der Browser kann die Eingabe dann sofort verarbeiten. Das verbessert die Reaktionsfähigkeit der Seite, messbar etwa an der Kennzahl Interaction to Next Paint (INP), ohne den Durchsatz unnötig zu senken.

Standardmäßig werden nur diskrete Ereignisse berücksichtigt, etwa keydown, mousedown oder click. Mit der Option includeContinuous: true zählen auch kontinuierliche Ereignisse wie mousemove, pointermove oder wheel. Diese treten sehr häufig auf und führen daher öfter zu true.

Rückgabewert

Typ
boolean
Beschreibung
true, wenn ein passendes Eingabeereignis auf Verarbeitung wartet, sonst false.

Beispiele

Lange Aufgabenliste reaktionsfreundlich abarbeiten

const yieldToMain = () => new Promise((resolve) => setTimeout(resolve, 0));

const processTasks = async (tasks) => {
  let pauses = 0;
  while (tasks.length > 0) {
    // Nur unterbrechen, wenn der Benutzer wirklich etwas eingibt
    if (navigator.scheduling?.isInputPending()) {
      pauses++;
      await yieldToMain();
      continue;
    }
    const task = tasks.shift();
    task();
  }
  console.log(`Fertig, ${pauses} Mal an den Browser abgegeben`);
};

const tasks = Array.from({ length: 10000 }, (_, i) => () => Math.sqrt(i) * Math.random());
processTasks(tasks);
Fertig, 0 Mal an den Browser abgegeben // ohne Interaktion; bei Klicks/Tastendrücken > 0

Feature-Erkennung mit Zeitbudget als Fallback

const hasInputPending = typeof navigator.scheduling?.isInputPending === 'function';
const BUDGET_MS = 50;

const shouldYield = (startTime) => {
  // Zeitbudget ist in jedem Browser eine harte Obergrenze
  if (performance.now() - startTime > BUDGET_MS) return true;
  return hasInputPending ? navigator.scheduling.isInputPending() : false;
};

const runChunked = async (items, handle) => {
  let start = performance.now();
  for (const item of items) {
    if (shouldYield(start)) {
      await new Promise((r) => setTimeout(r, 0));
      start = performance.now();
    }
    handle(item);
  }
};

console.log('isInputPending verfügbar:', hasInputPending);
runChunked([...Array(5000).keys()], (n) => n * n);
isInputPending verfügbar: true // in Chromium; in Firefox/Safari: false

Kontinuierliche Ereignisse einbeziehen

// z. B. während Drag-&-Drop auch auf Mausbewegungen reagieren
const renderHeavyFrame = () => {
  const start = performance.now();
  while (performance.now() - start < 100) {
    if (navigator.scheduling?.isInputPending({ includeContinuous: true })) {
      console.log('Eingabe (inkl. mousemove) wartet, breche ab');
      return false;
    }
  }
  return true;
};

document.addEventListener('pointerdown', () => {
  requestAnimationFrame(() => console.log('Frame komplett:', renderHeavyFrame()));
});
Eingabe (inkl. mousemove) wartet, breche ab Frame komplett: false

// Wichtig · Fallstricke

  • Kompatibilität: Die Methode ist experimentell und nur in Chromium-Browsern verfügbar (ab Version 87). Firefox und Safari unterstützen sie nicht. Prüfen Sie immer vorher mit navigator.scheduling?.isInputPending, ob sie existiert. Das Chrome-Team empfiehlt inzwischen eher scheduler.yield() bzw. regelmäßiges Abgeben nach einem festen Zeitbudget.
  • Nicht als einziges Kriterium verwenden: Auch ohne Eingabe muss der Browser rendern, Timer, Netzwerkantworten und requestAnimationFrame abarbeiten. Deshalb sollte man zusätzlich ein Zeitbudget einhalten, z. B. maximal 50 ms pro Aufgabe.
  • Falsch-negative Ergebnisse: Die Methode kann false liefern, obwohl eine Eingabe wartet. Das passiert etwa bei Eingaben, die noch nicht beim Renderer angekommen sind, oder bei Eingaben für ursprungsfremde iframes. Aus Datenschutzgründen werden nur Eingaben gemeldet, die an Dokumente desselben Ursprungs gehen.
  • Nur Hauptthread: Ein Aufruf in einem Worker ist nicht sinnvoll, weil dort navigator.scheduling nicht verfügbar ist.
  • includeContinuous: true liefert sehr häufig true. Das kann zu übermäßig vielen Unterbrechungen und damit zu geringem Durchsatz führen.