JavaScript-Ausführung optimieren

JavaScript löst häufig visuelle Änderungen aus. Manchmal geschieht dies direkt durch Stilmanipulationen und manchmal durch Berechnungen, die zu visuellen Änderungen führen, z. B. durch die Suche oder Sortierung von Daten. Falsch getimtes oder langlaufendes JavaScript ist eine häufige Ursache für Leistungsprobleme. Sie sollten versuchen, die Auswirkungen so gering wie möglich zu halten.

Paul Lewis

JavaScript löst häufig visuelle Änderungen aus. Manchmal geschieht dies direkt durch Stilmanipulationen und manchmal durch Berechnungen, die zu visuellen Änderungen führen, z. B. durch die Suche oder Sortierung von Daten. Falsch getimmtes oder langlaufendes JavaScript ist eine häufige Ursache für Leistungsprobleme. Sie sollten versuchen, die Auswirkungen so gering wie möglich zu halten.

Das JavaScript-Leistungsprofil kann eine gewisse Kunst sein, da der von Ihnen geschriebene JavaScript-Code nicht mit dem Code übereinstimmt, der tatsächlich ausgeführt wird. Moderne Browser verwenden JIT-Compiler und alle möglichen Optimierungen und Tricks, um die Ausführung so schnell wie möglich zu gestalten. Das verändert die Dynamik des Codes erheblich.

Es gibt jedoch einige Dinge, die Sie tun können, um die Ausführung von JavaScript in Ihren Apps zu verbessern.

Zusammenfassung

  • Verwenden Sie für visuelle Aktualisierungen nicht setTimeout oder setInterval, sondern immer requestAnimationFrame.
  • Verschieben Sie langlaufende JavaScript-Code aus dem Haupt-Thread in Webworker.
  • Verwenden Sie Mikroaufgaben, um DOM-Änderungen in mehreren Frames vorzunehmen.
  • Verwenden Sie die Zeitachse und den JavaScript-Profiler in den Chrome-Entwicklertools, um die Auswirkungen von JavaScript zu bewerten.

requestAnimationFrame für visuelle Änderungen verwenden

Wenn visuelle Änderungen auf dem Bildschirm auftreten, sollten Sie Ihre Arbeit zum richtigen Zeitpunkt für den Browser ausführen, also direkt zu Beginn des Frames. Nur wenn du requestAnimationFrame verwendest, kannst du sicher sein, dass dein JavaScript am Anfang eines Frames ausgeführt wird.

/**
    * 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 Samples können setTimeout oder setInterval für visuelle Änderungen wie Animationen verwenden. Das Problem dabei ist jedoch, dass der Rückruf irgendwo im Frame ausgeführt wird, möglicherweise direkt am Ende. Das kann oft dazu führen, dass ein Frame verpasst wird, was zu Rucklern führt.

setTimeout führt dazu, dass der Browser einen Frame verpasst.

Tatsächlich wurde in jQuery früher setTimeout für das animate-Verhalten verwendet. In Version 3 wurde requestAnimationFrame verwendet. Wenn Sie eine ältere Version von jQuery verwenden, können Sie einen Patch für die Verwendung von requestAnimationFrame anwenden. Wir empfehlen Ihnen dringend, dies zu tun.

Komplexität reduzieren oder Web-Worker verwenden

JavaScript wird im Hauptthread des Browsers ausgeführt, direkt neben Stilberechnungen, Layout und in vielen Fällen auch Malen. Wenn Ihr JavaScript lange ausgeführt wird, werden diese anderen Aufgaben blockiert, was möglicherweise zu Frame-Ausfällen führt.

Sie sollten taktisch vorgehen, wann und wie lange JavaScript ausgeführt wird. Wenn Sie beispielsweise eine Animation wie das Scrollen verwenden, sollte Ihr JavaScript idealerweise 3–4 ms betragen. Andernfalls riskieren Sie, zu viel Zeit in Anspruch zu nehmen. Wenn Sie gerade keine Aufträge haben, können Sie sich entspannter über die Zeit hinwegsetzen.

In vielen Fällen können Sie reine Rechenarbeit auf Webworker auslagern, wenn beispielsweise kein DOM-Zugriff erforderlich ist. Datenmanipulation oder ‑durchlauf, wie 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 Aufgaben passen in dieses Modell: Webworker haben keinen DOM-Zugriff. Wenn Ihre Arbeit im Hauptthread ausgeführt werden muss, sollten Sie einen Batch-Ansatz in Betracht ziehen, bei dem Sie die größere Aufgabe in Mikroaufgaben unterteilen, die jeweils nicht länger als einige Millisekunden dauern, und in requestAnimationFrame-Handlern über jeden Frame hinweg ausführen.

Dieser Ansatz hat Auswirkungen auf die UX und UI. Sie müssen dafür sorgen, dass die Nutzer wissen, dass eine Aufgabe verarbeitet wird, z. B. mithilfe eines Fortschritts- oder Aktivitätsindikators. Auf jeden Fall bleibt der Haupt-Thread Ihrer App kostenlos, sodass sie auf Nutzerinteraktionen reagieren kann.

Die „Frame-Steuer“ von JavaScript

Wenn Sie ein Framework, eine Bibliothek oder Ihren eigenen Code bewerten, ist es wichtig, zu ermitteln, wie viel es kostet, den JavaScript-Code Frame für Frame auszuführen. Das ist besonders wichtig bei leistungskritischen Animationen wie Übergängen oder Scrollen.

Im Bereich „Leistung“ der Chrome-Entwicklertools können Sie die Kosten Ihres JavaScripts am besten messen. Normalerweise erhalten Sie Low-Level-Einträge wie diesen:

Eine Leistungsaufzeichnung in den Chrome-Entwicklertools

Im Bereich Haupt finden Sie ein Flammendiagramm mit JavaScript-Aufrufen, mit dem Sie genau analysieren können, welche Funktionen aufgerufen wurden und wie lange jede davon gedauert hat.

Anhand dieser Informationen können Sie die Leistungsauswirkungen des JavaScript-Codes auf Ihre Anwendung bewerten und Hotspots finden und beheben, bei denen die Ausführung von Funktionen zu lange dauert. Wie bereits erwähnt, sollten Sie langlaufendes JavaScript entweder entfernen oder, falls dies nicht möglich ist, in einen Webworker verschieben, um den Haupt-Thread für andere Aufgaben freizugeben.

Im Hilfeartikel Einführung in die Analyse der Laufzeitleistung erfahren Sie, wie Sie den Bereich „Leistung“ verwenden.

Vermeiden Sie die Mikrooptimierung von JavaScript

Es ist zwar interessant zu wissen, dass der Browser eine Version einer Sache 100-mal schneller ausführen kann als eine andere, z. B. dass das Anfordern der offsetTop eines Elements schneller ist als das Berechnen von getBoundingClientRect(). Es ist jedoch fast immer so, dass Sie solche Funktionen nur wenige Male pro Frame aufrufen. Daher ist es normalerweise verschwendete Mühe, sich auf diesen Aspekt der JavaScript-Leistung zu konzentrieren. Normalerweise sparen Sie nur Bruchteile von Millisekunden.

Wenn Sie ein Spiel oder eine rechenintensive Anwendung entwickeln, sind Sie wahrscheinlich eine Ausnahme von dieser Empfehlung, da Sie in der Regel viele Berechnungen in einen einzelnen Frame einpassen. In diesem Fall ist jede Verbesserung hilfreich.

Kurz gesagt: Sie sollten Mikrooptimierungen mit Bedacht einsetzen, da sie in der Regel nicht zur Art der Anwendung passen, die Sie entwickeln.