Event Listener und Web Worker

Ich habe kürzlich die Web Workers API herausgefunden . Es ist bedauerlich, dass ich mir vorher nicht die Zeit für dieses gut unterstützte Tool genommen habe. Moderne Webanwendungen stellen hohe Anforderungen an die Funktionen des JavaScript-Hauptthreads. Dies wirkt sich auf die Produktivität von Projekten und deren Fähigkeit aus, eine komfortable Benutzererfahrung zu gewährleisten. Web Worker sind genau das, was heutzutage Entwicklern helfen kann, schnelle und bequeme Webprojekte zu erstellen.



In dem Moment habe ich alles verstanden


Web Worker haben viele positive Eigenschaften. Aber ich habe ihre Nützlichkeit wirklich erkannt, als ich auf eine Situation stieß, in der eine bestimmte Anwendung mehrere DOM-Ereignis-Listener verwendet. B. Ereignisse beim Senden von Formularen, Ändern der Fenstergröße, Klicken auf Schaltflächen. Alle diese Listener müssen im Hauptthread arbeiten. Wenn der Hauptthread mit bestimmten Vorgängen überlastet ist, die lange dauern, hat dies eine negative Auswirkung auf die Reaktionsrate der Ereignis-Listener auf Benutzereinflüsse. Die Anwendung "verlangsamt sich", Ereignisse warten auf die Freigabe des Hauptthreads.

Ich muss zugeben, der Grund, warum ich mich so für Event-Listener interessierte, war, dass ich anfangs falsch verstanden habe, welche Aufgaben Web-Worker lösen sollen. Zuerst dachte ich, sie könnten dazu beitragen, die Geschwindigkeit der Codeausführung zu verbessern. Ich dachte, dass eine Anwendung in einem bestimmten Zeitraum viel mehr kann, wenn einige Fragmente ihres Codes parallel in separaten Threads ausgeführt werden. Während der Ausführung von Webprojektcode ist es jedoch durchaus üblich, dass Sie auf ein Ereignis warten müssen, bevor Sie etwas tun. Angenommen, das DOM muss erst aktualisiert werden, nachdem einige Berechnungen abgeschlossen sind. Da ich das wusste, glaubte ich naiv, dass es keinen Sinn macht, die Ausführung von Code auf einen separaten Thread zu übertragen, wenn ich auf jeden Fall warten muss.

Hier ist ein Beispiel für Code, den Sie hier abrufen können:

const calculateResultsButton = document.getElementById('calculateResultsButton'); const openMenuButton = document.getElementById('#openMenuButton'); const resultBox = document.getElementById('resultBox'); calculateResultsButton.addEventListener('click', (e) => {     // "    -, ,   ,     //   DOM   ?"     const result = performLongRunningCalculation();     resultBox.innerText = result; }); openMenuButton.addEventListener('click', (e) => {     //      . }); 

Hier aktualisiere ich den Text im Feld, nachdem einige Berechnungen abgeschlossen sind, vermutlich langwierig. Es scheint sinnlos zu sein, diesen Code in einem separaten Thread auszuführen, da das DOM nicht aktualisiert wird, bevor dieser Code vollständig ist. Aus diesem Grund entscheide ich mich natürlich, dass dieser Code synchron ausgeführt werden muss. Als ich jedoch einen solchen Code sah, verstand ich zunächst nicht, dass andere Ereignis-Listener nicht gestartet werden, solange der Haupt-Thread blockiert ist. Dies bedeutet, dass auf der Seite "Bremsen" angezeigt werden.

So "verlangsamen" Sie die Seite


Hier ist ein CodePen-Projekt, das das oben Genannte demonstriert.


Ein Projekt, das eine Situation zeigt, in der Seiten langsam sind

Durch Drücken der Freeze die Synchronaufgabe von der Anwendung gelöst. All dies dauert 3 Sekunden (es simuliert die Leistung von langwierigen Berechnungen). Wenn Sie gleichzeitig auf die Schaltfläche Increment klicken, wird der Wert im Feld Klickanzahl erst nach Ablauf von 3 Sekunden aktualisiert. Ein neuer Wert, der der Anzahl der Klicks auf Increment wird erst nach Increment drei Sekunden in dieses Feld geschrieben. Der Hauptstrom ist während einer Pause blockiert. Infolgedessen funktioniert alles im Anwendungsfenster nicht mehr. Die Anwendungsschnittstelle ist eingefroren. Ereignisse, die während des „Einfrierens“ auftreten, warten auf die Gelegenheit, die Ressourcen des Hauptstroms zu nutzen.

Wenn Sie auf Freeze klicken und versuchen, resize me! zu resize me! Auch hier ändert sich die Größe des Feldes nicht, bis drei Sekunden vergangen sind. Danach ändert sich jedoch die Größe des Feldes, es ist jedoch nicht erforderlich, über eine „Glätte“ der Benutzeroberfläche zu sprechen.

Ereignis-Listener sind ein viel größeres Phänomen, als es auf den ersten Blick erscheinen mag


Jeder Benutzer möchte nicht mit einer Site arbeiten, die sich wie im vorherigen Beispiel verhält. Hier kommen aber nur wenige Event-Listener zum Einsatz. In der realen Welt sprechen wir von völlig anderen Maßstäben. Ich habe mich für die getEventListeners Methode in Chrome entschieden und mithilfe des folgenden Skripts die Anzahl der Ereignis-Listener ermittelt, die an die DOM-Elemente verschiedener Seiten angehängt sind. Dieses Skript kann direkt in der Entwicklertool-Konsole ausgeführt werden. Da ist er:

 Array  .from([document, ...document.querySelectorAll('*')])  .reduce((accumulator, node) => {    let listeners = getEventListeners(node);    for (let property in listeners) {      accumulator = accumulator + listeners[property].length    }    return accumulator;  }, 0); 

Ich habe dieses Skript auf verschiedenen Seiten ausgeführt und herausgefunden, wie viele Ereignis-Listener auf diesen Seiten verwendet wurden. Die Ergebnisse meines Experiments sind in der folgenden Tabelle gezeigt.
App
Anzahl der Ereignislistener
Dropbox
602
Google-Nachrichten
581
Reddit
692
YouTube
6054 (!!!)

Sie können nicht auf bestimmte Nummern achten. Die Hauptsache hier ist, dass wir über eine sehr große Anzahl von Event-Zuhörern sprechen. Infolgedessen reagieren alle diese Listener nicht mehr auf Benutzereinflüsse, wenn mindestens ein längerer Vorgang in der Anwendung fehlschlägt. Dies gibt Entwicklern viele Möglichkeiten, die Benutzer ihrer Anwendungen zu verärgern.

Beseitigen Sie mit Web-Workern die „Bremsen“


In Anbetracht all der obigen Umstände schreiben wir das vorherige Beispiel neu. Hier ist seine neue Version. Es sieht genauso aus wie das alte, ist aber anders angeordnet. Nun wurde nämlich die Operation, die zum Blockieren des Hauptthreads verwendet wurde, in einen eigenen Thread verschoben. Wenn Sie mit diesem Beispiel dasselbe tun wie mit dem vorherigen, können Sie schwerwiegende positive Unterschiede feststellen. Wenn Sie nach dem Klicken auf die Schaltfläche " Freeze auf " Increment klicken, wird das Feld "Klickanzahl" aktualisiert (nach Abschluss des Webworkers wird in jedem Fall die Zahl 1 zum Wert " Click Count hinzugefügt). Das gleiche gilt für die resize me! . In einem separaten Thread ausgeführter Code blockiert keine Ereignis-Listener. Dies ermöglicht, dass alle Elemente der Seite auch während der Ausführung einer Operation funktionsfähig bleiben, bei der die Seite zuvor einfach "eingefroren" wurde.

Hier ist der JS-Code für dieses Beispiel:

 const button1 = document.getElementById('button1'); const button2 = document.getElementById('button2'); const count = document.getElementById('count'); const workerScript = `  function pause(ms) {    let time = new Date();    while ((new Date()) - time <= ms) {}               }  self.onmessage = function(e) {    pause(e.data);    self.postMessage('Process complete!');  } `; const blob = new Blob([  workerScript, ], {type: "text/javascipt"}); const worker = new Worker(window.URL.createObjectURL(blob)); const bumpCount = () => {  count.innerText = Number(count.innerText) + 1; } worker.onmessage = function(e) {  console.log(e.data);  bumpCount(); } button1.addEventListener('click', async function () {  worker.postMessage(3000); }); button2.addEventListener('click', function () {  bumpCount(); }); 

Wenn Sie sich ein wenig mit diesem Code befassen, werden Sie feststellen, dass die Web Workers-API zwar übersichtlicher und komfortabler sein könnte, die Arbeit jedoch nicht besonders beängstigend ist. Dieser Code sieht wahrscheinlich beängstigend aus, da es sich um eine einfache, schnell geschriebene Demo handelt. Um die Benutzerfreundlichkeit der API zu verbessern und die Arbeit mit Web-Workern zu vereinfachen, können Sie einige zusätzliche Tools verwenden. Zum Beispiel erschien mir Folgendes interessant:


Zusammenfassung


Wenn es sich bei Ihrer Webanwendung um ein typisches modernes Projekt handelt, ist es sehr wahrscheinlich, dass es viele Ereignis-Listener gibt. Es ist auch möglich, dass es im Haupt-Thread viele Berechnungen ausführt, die durchaus in anderen Threads durchgeführt werden können. Infolgedessen können Sie sowohl Ihren Benutzern als auch den Zuhörern von Veranstaltungen einen guten Service bieten und Web-Mitarbeitern „schwere“ Berechnungen anvertrauen.

Es ist erwähnenswert, dass eine übermäßige Begeisterung für Web-Worker und das Entfernen von allem, was nicht direkt mit der Benutzeroberfläche für Web-Worker zusammenhängt, wahrscheinlich nicht die beste Idee ist. Eine solche Bearbeitung eines Antrags kann viel Zeit und Mühe erfordern, der Projektcode wird komplizierter und die Vorteile einer solchen Konvertierung sind sehr gering. Stattdessen könnte es sich lohnen, zunächst nach einem wirklich „schweren“ Code zu suchen und ihn den Web-Mitarbeitern zur Verfügung zu stellen. Mit der Zeit wird die Idee, Web-Worker zu verwenden, vertrauter und Sie werden wahrscheinlich bereits in der Phase des Interface-Designs davon geleitet.

Wie auch immer, ich empfehle Ihnen, die Web Workers-API zu verstehen. Diese Technologie wird von zahlreichen Browsern unterstützt, und die Leistungsanforderungen an moderne Webanwendungen steigen. Daher haben wir keinen Grund, das Studium von Tools wie Webworkern abzulehnen.

Sehr geehrte Leser! Verwenden Sie in Ihren Projekten Web-Worker?


Source: https://habr.com/ru/post/de479268/


All Articles