Bessere JS-Planung mit isInputPending()

Eine neue JavaScript API, mit der Sie einen Kompromiss zwischen Ladeleistung und Reaktionsfähigkeit bei der Eingabe vermeiden können.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

Ein schnelles Laden ist schwierig. Websites, die ihre Inhalte derzeit mithilfe von JavaScript rendern zwischen Lastleistung und Eingabequalität Reaktionsfähigkeit: entweder alle Arbeiten ausführen, die für die Displaywerbung erforderlich sind auf einmal (bessere Ladeleistung, schlechtere Reaktionsfähigkeit bei der Eingabe) oder die Arbeit in kleinere Aufgaben aufteilen, um reaktionsschnell Eingabe und Darstellung (schlechte Ladeleistung, bessere Eingabe Sensibilität).

Um diesen Kompromiss zu vermeiden, schlug Facebook vor und implementierte isInputPending()-API in Chromium verwenden, um die Reaktionsfähigkeit zu verbessern, ohne nachtragend sein. Aufgrund von Feedback zum Ursprungstest haben wir einige Aktualisierungen am API verfügbar. Wir freuen uns, Ihnen mitteilen zu können, dass die API jetzt standardmäßig in Chromium ausgeliefert wird. 87!

Browserkompatibilität

Unterstützte Browser

  • Chrome: 87 <ph type="x-smartling-placeholder">
  • Edge: 87. <ph type="x-smartling-placeholder">
  • Firefox: nicht unterstützt <ph type="x-smartling-placeholder">
  • Safari: wird nicht unterstützt. <ph type="x-smartling-placeholder">

Quelle

isInputPending() wird in Chromium-basierten Browsern ab Version 87 ausgeliefert. Kein anderer Browser hat die Absicht signalisiert, die API zu versenden.

Hintergrund

Die meiste Arbeit im heutigen JS-System wird in einem einzigen Thread erledigt: dem Hauptthread. Dies bietet Entwicklern ein robustes Ausführungsmodell, (insbesondere die Reaktionsfähigkeit) kann drastisch beeinträchtigt werden, wenn das Skript für einen längeren Zeitraum . Wenn die Seite viel Arbeit macht, während ein Eingabeereignis ausgelöst wird, Beispiel: Die Seite verarbeitet das Klickeingabeereignis erst nach der abgeschlossen wird.

Die aktuelle Best Practice besteht darin, dieses Problem anzugehen, indem Sie die um JavaScript in kleinere Blöcke aufzuteilen. Während die Seite geladen wird, und dann die Kontrolle an den Browser übergeben. Die kann er die Eingangsereigniswarteschlange überprüfen und feststellen, ob dort was sie der Seite mitteilen muss. Anschließend führt der Browser wieder die werden JavaScript blockiert. Das hilft, kann aber andere Probleme verursachen.

Jedes Mal, wenn die Seite dem Browser die Kontrolle übergibt, dauert es einige Zeit, um die Eingabeereigniswarteschlange zu überprüfen, Ereignisse zu verarbeiten und die nächsten JavaScript-Block. Während der Browser schneller auf Ereignisse reagiert, verlangsamt. Und wenn wir zu oft nachgeben, lädt zu langsam. Wenn wir seltener nachgeben, dauert es länger, bis der Browser auf Nutzerereignisse reagieren und die Nutzer frustriert sind. Kein Spaß.

Ein Diagramm, das zeigt, dass der Browser beim Ausführen langer JS-Aufgaben weniger Zeit zum Auslösen von Ereignissen hat.

Bei Facebook wollten wir sehen, wie die Dinge aussehen würden, wenn wir eine neuen Ansatz zum Laden, der diesen frustrierenden Kompromiss eliminieren würde. Mi. unsere Freunde bei Chrome darüber gesprochen und einen Vorschlag für isInputPending(). Die isInputPending() API verwendet als Erstes das Konzept der Nutzereingaben im Web unterbrochen werden und JavaScript kann Eingaben prüfen, ohne dass der Browser dies nachlässt.

Ein Diagramm, das zeigt, dass „isInputPending()“ Ihrem JS-Code ist, zu prüfen, ob eine Nutzereingabe ausstehend ist, ohne dass die Ausführung vollständig an den Browser erfolgt.

Aufgrund des Interesses an der API arbeiteten wir mit unseren Chrome-Kollegen zusammen. um die Funktion in Chromium zu implementieren und zu veröffentlichen. Mit Chrome die Patches wurden einem Ursprungstest unterzogen Mit dieser Funktion kann Chrome Änderungen testen und Feedback von Entwicklern einholen. bevor eine API vollständig veröffentlicht wird).

Wir haben jetzt Feedback vom Ursprungstest und von den anderen Mitgliedern der W3C Web Performance Working Group und hat Änderungen an der API implementiert.

Beispiel: Yieldier-Planer

Angenommen, ihr müsst eine Menge Arbeit erledigen, um das Display zu blockieren. zum Beispiel das Generieren von Markup aus Komponenten, das Ausschließen von Primzahlen einfach ein cooles Ladesymbol zeichnen. Jedes davon wird in eine eigene Arbeitselement. Lassen Sie uns mithilfe des Planermusters skizzieren, wie wir eine hypothetische processWorkQueue()-Funktion:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Wenn processWorkQueue() später in einer neuen Makroaufgabe über setTimeout() aufgerufen wird, um dem Browser die Möglichkeit zu geben, auf Eingaben zu reagieren, Event-Handler ausführen, bevor die Arbeit fortgesetzt wird) ohne Unterbrechung. Allerdings kann es sein, dass der Termin durch andere Aufgaben die Steuerung der Ereignisschleife wünscht, oder zusätzliche QUANTUM Millisekunden erreichen der Ereignislatenz.

Das ist in Ordnung, aber können wir das besser machen? Auf jeden Fall!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Mit einem Anruf bei navigator.scheduling.isInputPending() können wir schneller auf Eingaben reagieren und gleichzeitig dafür sorgen, ohne Unterbrechung ausgeführt wird. Wenn wir mit irgendetwas nichts bis die Arbeit abgeschlossen ist, können wir die Zahl auch die Länge von QUANTUM.

Standardmäßig ist „Kontinuierlich“ Ereignisse werden von isInputPending() nicht zurückgegeben. Diese sind mousemove, pointermove und weitere. Wenn Sie mit Ihrem Budget auch diese verwenden, kein Problem. Durch Bereitstellen eines Objekts für isInputPending() mit includeContinuous ist auf true festgelegt. Jetzt kann es losgehen:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Fertig! Frameworks wie React unterstützen isInputPending() in der mit ähnlicher Logik. Hoffentlich führt dies Entwickler, die diese Frameworks nutzen, um von isInputPending() zu profitieren ohne aufwendige Umformulierungen hinter die Kulissen.

Ertrag ist nicht immer schlecht

Zu beachten ist, dass weniger Einnahme nicht für jeden Verwendungszweck die richtige Lösung ist. Fall. Es gibt viele weitere Gründe, die Kontrolle an den Browser zurückzugeben, Eingabeereignisse verarbeiten, etwa zum Ausführen von Renderings und Ausführen anderer Skripte auf auf der Seite.

Es gibt Fälle, in denen der Browser den Status "Ausstehend" nicht richtig zuordnen kann. Eingabeereignissen. Vor allem das Einrichten komplexer Clips und Masken für ursprungsübergreifende iFrames melden möglicherweise falsch negative Ergebnisse (d.h., isInputPending() könnte eine unerwartete Ausgabe zurückgeben) false festlegen. Achten Sie darauf, dass Sie oft genug ertragen, wenn Ihre Website erfordert Interaktionen mit stilisierten Subframes.

Achten Sie auch auf andere Seiten mit einer gemeinsamen Ereignisschleife. Auf Plattformen wie wie Chrome für Android, kommt es häufig vor, dass mehrere Ursprünge ein Ereignis teilen, Schleife isInputPending() gibt niemals true zurück, wenn die Eingabe an eine ursprungsübergreifenden Frame zu verstehen. Daher können Seiten im Hintergrund den die Reaktionszeit der Seiten im Vordergrund. Möglicherweise möchten Sie Ihre Kampagnen häufiger, wenn Sie im Hintergrund mit der Page Visibility API arbeiten.

Wir empfehlen dir, isInputPending() mit Bedacht zu verwenden. Wenn es keine zu verhindern, und seien Sie freundlich zu anderen in der Ereignisschleife, indem Sie häufiger erbringt. Lange Aufgaben können schädlich sein.

Feedback

  • Feedback zu den Spezifikationen in der is-input-pending-Repository.
  • Wenden Sie sich an @acomminos, einen der Spezifikationsautoren. auf Twitter.

Fazit

Wir freuen uns über die Einführung von isInputPending(). um sie noch heute zu nutzen. Mit dieser API erstellt Facebook zum ersten Mal eine Web-API entwickelt und sie von der Ideenfindung über den Standardvorschlag bis hin zur der Versand in einem Browser. Wir möchten uns bei allen bedanken, die uns geholfen haben, und ein besonderes Dankeschön an alle bei Chrome, die uns geholfen haben, diese Idee umzusetzen und zu versenden.

Hero-Foto von Will H McMahan auf Unsplash (Unsplash).