JavaScript-Ausführung optimieren

JavaScript löst oft visuelle Änderungen aus. Manchmal liegt dies direkt an Stilbearbeitungen, manchmal sind es Berechnungen, die zu visuellen Änderungen führen, z. B. durch Suchen oder Sortieren von Daten. Eine häufige Ursache für Leistungsprobleme sind JavaScript-Code mit fehlerhafter oder langer Ausführungszeit. Sie sollten die Auswirkungen so weit wie möglich minimieren.

Paul Lewis
Paul Lewis

JavaScript löst oft visuelle Änderungen aus. Manchmal erfolgt dies direkt durch Stiländerungen oder Berechnungen, die zu visuellen Änderungen führen, z. B. durch Suchen oder Sortieren von Daten. Falsche Zeitüberschreitungen oder lang andauernde JavaScript-Codes sind eine häufige Ursache für Leistungsprobleme. Sie sollten die Auswirkungen so weit wie möglich minimieren.

Die Erstellung von JavaScript-Leistungsprofilen kann eine Kunst sein, da der von Ihnen geschriebene JavaScript-Code nichts mit dem Code zu vergleichen ist, der tatsächlich ausgeführt wird. Moderne Browser verwenden JIT-Compiler und alle möglichen Optimierungen und Tricks, um Ihnen die schnellstmögliche Ausführung zu ermöglichen. Dadurch ändert sich die Dynamik des Codes erheblich.

Trotzdem gibt es einige Dinge, die Sie auf jeden Fall tun können, damit Ihre Apps JavaScript gut ausführen.

Zusammenfassung

  • Vermeiden Sie setTimeout oder setInterval für visuelle Aktualisierungen. Verwenden Sie stattdessen immer requestAnimationFrame.
  • Verschieben Sie lang andauernden JavaScript-Code aus dem Hauptthread auf Web Worker.
  • Mit Mikroaufgaben können Sie DOM-Änderungen über mehrere Frames hinweg vornehmen.
  • Mit der Zeitachse der Chrome-Entwicklertools und dem JavaScript Profiler können Sie die Auswirkungen von JavaScript analysieren.

requestAnimationFrame für visuelle Änderungen verwenden

Wenn visuelle Änderungen auf dem Bildschirm auftreten, möchten Sie Ihre Arbeit zum richtigen Zeitpunkt für den Browser erledigen, also direkt am Anfang des Frames. Die einzige Möglichkeit sicherzustellen, dass Ihr JavaScript am Anfang eines Frames ausgeführt wird, ist die Verwendung von requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

Frameworks oder Beispiele können setTimeout oder setInterval verwenden, um visuelle Änderungen wie Animationen vorzunehmen. Das Problem dabei ist jedoch, dass der Callback an einer Stelle im Frame ausgeführt wird, möglicherweise direkt am Ende. Dies kann häufig dazu führen, dass wir einen Frame übersehen und zu Verzögerungen führen.

setTimeout führt dazu, dass der Browser einen Frame überlässt.

Tatsächlich hat jQuery für ihr animate-Verhalten setTimeout verwendet. Es wurde in Version 3 zur Verwendung von requestAnimationFrame geändert. Wenn Sie eine ältere Version von jQuery verwenden, können Sie sie für die Verwendung von requestAnimationFrame patchen. Dies wird dringend empfohlen.

Komplexität reduzieren oder Web Worker verwenden

JavaScript wird im Hauptthread des Browsers ausgeführt, direkt neben Stilberechnungen, Layout und in vielen Fällen Paint. Wenn Ihr JavaScript über einen längeren Zeitraum ausgeführt wird, blockiert es diese anderen Aufgaben und kann dazu führen, dass Frames übersehen werden.

Sie sollten taktisch überlegen, wann und wie lange JavaScript ausgeführt wird. Wenn Sie sich beispielsweise in einer Animation wie Scrollen befinden, sollten Sie den JavaScript-Code im Bereich von 3–4 ms beibehalten. Bei einer längeren Dauer riskieren Sie, zu viel Zeit in Anspruch zu nehmen. Bei Inaktivität können Sie sich weniger Zeit nehmen.

In vielen Fällen können Sie reine Rechenarbeit in Web Worker verschieben, wenn beispielsweise kein DOM-Zugriff erforderlich ist. Datenmanipulation und -durchlauf, wie das Sortieren oder Suchen, eignen sich oft gut für dieses Modell, ebenso wie das Laden und die Modellgenerierung.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

Nicht alle Arbeiten entsprechen diesem Modell: Web Worker haben keinen DOM-Zugriff. Erwägen Sie einen Batching-Ansatz, wenn sich Ihre Arbeit im Hauptthread befinden muss. Dabei segmentieren Sie die größere Aufgabe in Mikroaufgaben, die jeweils nicht länger als ein paar Millisekunden dauern, und in requestAnimationFrame-Handlern für jeden Frame ausgeführt werden.

Dieser Ansatz hat Auswirkungen auf die Nutzererfahrung und die Benutzeroberfläche und Sie müssen dem Nutzer mithilfe eines Fortschritts- oder Aktivitätsindikators mitteilen, dass eine Aufgabe verarbeitet wird. In jedem Fall bleibt der Hauptthread Ihrer Anwendung bei diesem Ansatz kostenlos, sodass sie auf Nutzerinteraktionen reagiert.

Die „Frame-Steuer“ Ihres JavaScripts kennen

Wenn Sie ein Framework, eine Bibliothek oder Ihren eigenen Code bewerten, ist es wichtig, zu wissen, wie viel die Ausführung des JavaScript-Codes auf Frame für Frame-Basis kostet. Dies ist besonders wichtig bei leistungskritischen Animationen wie Übergängen oder Scrollen.

Im Bereich „Leistung“ der Chrome-Entwicklertools lässt sich die Kosten Ihres JavaScript-Codes am besten messen. Normalerweise erhalten Sie Low-Level-Einträge wie diese:

Leistungsaufzeichnung in den Chrome-Entwicklertools

Der Bereich Main enthält ein Flame-Diagramm der JavaScript-Aufrufe, damit Sie genau analysieren können, welche Funktionen aufgerufen wurden und wie lange sie jeweils gedauert haben.

Anhand dieser Informationen können Sie die Auswirkungen des JavaScript-Codes auf die Leistung Ihrer Anwendung bewerten und damit beginnen, Hotspots zu finden und zu beheben, bei denen die Ausführung von Funktionen zu lange dauert. Wie bereits erwähnt, sollten Sie versuchen, entweder lang andauernden JavaScript-Code zu entfernen oder, wenn dies nicht möglich ist, ihn in einen Web Worker zu verschieben, der den Hauptthread für andere Aufgaben freigibt.

Unter Erste Schritte mit der Analyse der Laufzeitleistung erfahren Sie, wie Sie das Feld „Leistung“ verwenden.

Mikrooptimierung von JavaScript vermeiden

Es mag cool sein zu wissen, dass der Browser eine Version einer Sache 100-mal schneller als eine andere ausführen kann, zum Beispiel, dass das Anfordern der offsetTop eines Elements schneller ist als die Berechnung von getBoundingClientRect(). Es stimmt aber fast immer, dass Funktionen wie diese nur wenige Male pro Frame aufgerufen werden. Daher ist es normalerweise verschwendete Mühe, sich auf diesen Aspekt der JavaScript-Leistung zu konzentrieren. Normalerweise sparen Sie nur Sekundenbruchteile.

Wenn Sie ein Spiel oder eine rechenintensive Anwendung entwickeln, sind Sie wahrscheinlich eine Ausnahme von dieser Anleitung, da Sie in der Regel viele Berechnungen in einen einzelnen Frame packen müssen und in diesem Fall alles hilft.

Kurz gesagt, sollten Sie bei Mikrooptimierungen vorsichtig sein, da diese normalerweise nicht der Art der von Ihnen erstellten Anwendung zugeordnet werden können.