Path: csiph.com!weretis.net!feeder4.news.weretis.net!feeder5.news.weretis.net!news.solani.org!.POSTED!not-for-mail From: Thomas 'PointedEars' Lahn Newsgroups: de.comp.lang.javascript Subject: Re: Gluecksrad Date: Fri, 18 Nov 2016 21:45 +0100 Organization: PointedEars Software (PES) Lines: 279 Message-ID: <2780097.5fSG56mABF@PointedEars.de> References: <3307965.kQq0lBPeGt@PointedEars.de> <1a76ea3d-6da6-d368-78b5-3073fe844d07@arnowelzel.de> <10725728.O9o76ZdvQC@PointedEars.de> <4986933.DvuYhMxLoT@PointedEars.de> <1763443.oMNUckLgyt@PointedEars.de> Reply-To: Thomas 'PointedEars' Lahn Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8Bit X-Trace: solani.org 1479501901 13327 eJwFwQkBwDAIA0BLvIHJKaPxL6F36VD8FUhEMqmxzRvKD+XinCOFM7KhJl7bg7u2xqFX730ZNxFe (18 Nov 2016 20:45:01 GMT) X-Complaints-To: abuse@news.solani.org NNTP-Posting-Date: Fri, 18 Nov 2016 20:45:01 +0000 (UTC) User-Agent: KNode/4.14.2 X-User-ID: eJwNycEBwCAIA8CVoCEBxlEr+4/Q3vcIuU6GqOBwam+TW5J1oXYYHnbNeaFc41u4HmZSr4XuTK946fvw4u8PHOcURw== Cancel-Lock: sha1:LexDyVELbqTZHBoUyaXDEuH6pJM= X-NNTP-Posting-Host: eJwFwQkBwDAIA0BL4w9yWmj8S9hdWEpOeUZ6MLgQu64LH04GzToNb0Hp+5VaACw9yte95/0XRBEh Xref: csiph.com de.comp.lang.javascript:4814 Arno Welzel wrote: > Thomas 'PointedEars' Lahn schrieb: >> Arno Welzel wrote: >>> […] Performanceprobleme erkenne in der *Praxis* ich keine, >>> selbst wenn die Performanceunterschiede verschiedener Umsetzungen >>> theoretisch messbar sein mögen. >> >> Je länger man Deine Website anschaut, umso mehr Arbeitsspeicher frisst >> sie, weil immer mehr davon für immer neue Objekte reserviert werden muss >> – >> unnötiger Weise. Irgendwann wird dann der Tab oder der Browser vom OOM- >> Killer abgeschossen oder das System hängt. Das ist nicht nur theoretisch >> messbar (was BTW ein Widerspruch in sich ist; Messungen sind immer >> Praxis). > > Aha - und welcher Umgebung soll das sein? Chromium “53.0.2785.143 Built on 8.6, running on Debian stretch/sid (64- bit).” Nach 10 Minuten auf der Website ist der Speicherbedarf des Tabs (siehe Chromium Task-Manager, Shift+Esc) um ca. 7.14 MiB angestiegen. Man kann beobachten, wie der Speicherbedarf im 5-Sekunden-Takt von kontinierlich ansteigt. Das korreliert genau mit dem wiederholten überflüssigen Erzeugen der XHR-Instanz, wie man in der Spalte “Network” und mit “Profiles”, “Record Allocation Timeline” sieht (zuerst steigt der Speicherbedarf, dann gibt es die Netzwerkaktivität). Start: 21:12 CET Spalte “Memory”: 60'552 KiB Ende: 21:22 CET Spalte “Memory”: 67'312 KiB Das Ergebnis ist zuverlässig (nach “uncached reload”) reproduzierbar. Und dabei habe ich *nichts* auf der Website oder im Browser gemacht ausser gelegentlich zwischen Browser-Task-Manager und Newsreader (nicht im Browser) hin- und herzuschalten. Das Problem gibt es nicht nur auf Deiner Website (sondern besonders problematisch auch bei Facebook und Twitter, die auch über XHR den Status aktualisieren), aber die Ursache ist dieselbe. Denn wenn man nichts macht oder XHR richtig macht, passiert es nicht. > Hier mit Linux Mint 14 und Firefox 49 lässt sich das nicht reproduzieren. > Auch nach mehrere Stunden ist der Speicherbedarf nicht angestiegen, Wie misst Du das? > der OOM-Killer wird logischerweise auch nicht aktiv und ein > hängendendes System habe ich ebenfalls nicht. Du solltest auch nicht davon ausgehen, dass Deine Website die einzige ist, die im Browser angezeigt wird. > Eine kurzer Vergleich Was heisst „kurz“? > mit Windows 7 und Firefox 49 sowie IE 11 wie auch Firefox unter MacOS hat > ebenfalls keine Auffälligkeiten gezeigt. Hast Du nur in Firefox 49 und IE 11 getestet? >> Über den Rest Deiner DAU-dummen Reaktion breiten wir lieber den Mantel >> des Schweigens. > > Zeige mir, was an der verwendeten switch()-Anweisung konkret *falsch* > ist oder lass' es. Ich schrieb „_unnötig bis_ falsch“. Es hilft Deiner Argumentation nicht, wenn Du mich verkürzt wiedergibst. Weshalb ich das schrieb, dachte ich, sei offensichtlich. Zur besseren Lesbarkeit habe die Einrückung reduziert und den Quelltext lesbarer formatiert (auch etwas, an dem Du noch arbeiten kannst): | req.onreadystatechange = function () { | switch (req.readyState) | { | case 4: | […] | break; | | default: | return false; | break; | } | }; Die switch-Anweisung befindet sich *alleinstehend* in einer *Funktion*. Somit ist die return-Anweisung überflüssig, denn nach der switch-Anweisung wird die Funktion ohnehin verlassen: req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; default: break; } }; Schreibt man die return-Anweisung hin, ist die zweite break-Anweisung überflüssig, denn die Funktion wird ja schon durch die return-Anweisung verlassen: req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; default: return false; } }; Es handelt sich ausserdem um einen Event-Listener, der an eine Event- Handler-Eigenschaft zugewiesen wurde. Da ist es wesentlich, wie der Rückgabewert aussieht, denn ein Rückgabewert kann zur Vermeidung der Standardaktion führen (das ist nicht immer “false”, kann auch “true” sein). Man sollte also nicht einfach blind einen Wert zurückgeben, weil „das schon funktioniert“, sondern sich diesen gut überlegen, und im Zweifelsfall "gar nichts" (d. h. “undefined”) zurückgeben: req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; default: return; } }; Das ist aber schon das Standardverhalten ohne return-Anweisung: req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; default: } }; Will man die Funktion erweitern, also Anweisungen nach der switch-Anweisung hinzufügen, muss man sich daran erinnern, dass da noch eine return-Anweisung in der switch-Anweisung steht; solange wundert man sich, weshalb der neue Code manchmal nicht ausgeführt wird: req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; default: return false; break; } /* * _Halb_toter Code; kann je nach readyState Probleme verursachen; * schlecht testbar. */ }; In der default-Klausel steht kein (sinnvoller) Code, sie kann also genausogut weggelassen werden. req.onreadystatechange = function () { switch (req.readyState) { case 4: […] break; } }; Lässt man die default-Klausel nicht weg, so lässt sich dies weniger fehlerträchtig schreiben: req.onreadystatechange = function () { if (req.readyState == 4) { // … } else { return; } /* * _Halb_toter Code; kann je nach readyState Probleme verursachen; * schlecht testbar. */ }; Spätestens jetzt sieht man, dass man den Code stark vereinfachen kann, wodurch er weniger fehlerträchtig und leichter wartbar wird: req.onreadystatechange = function () { if (req.readyState == 4) { // … } /* * Evtl. Code hier nun nicht mehr tot; gut testbar. */ }; oder (“early return”, “gauntlet“): req.onreadystatechange = function () { if (req.readyState != 4) return; // … /* * Evtl. Code hier nun nicht mehr tot; gut testbar. */ }; > Und zum Thema "Sicherheit": zeige mir, welches ausnutzbare(!") > Sicherheitsproblem bei meiner Verwendung von windows.setTimeout() > übergibt. Erstens heisst es window.setTimeout(). Zweitens kann der Code, wenn er als String übergeben wird, nur auf global verfügbare Symbole zugreifen. Das ist für sich schon ein Problem, auch ein Sicherheitsproblem. Denn jedes fremde Script, was im Dokument geladen wird, kann die Werte im globalen Ausführungskontext manipulieren, also zum Beispiel die Funktion überschreiben. Sogar jede Extension und jedes Plugin kann das, weil der globale Kontext an das Tab/Fenster gekoppelt ist. Drittens ergibt sich das ausnutzbare Sicherheitsproblem dadurch, dass Du als Autor nicht Sicherheitsfeatures des Browsers wie Content Security Policy nutzen kannst, solange Du den Code als String übergibst (siehe Referenz). Kein heutzutage sinnvoll scriptbarer Browser erfordert noch die String- Variante. Wie aus der ECMAScript Support Matrix hervorgeht (ausnahmsweise, weil dieses Feature mit dem Netscape DOM eingeführt wurde, bevor es die Trennung zwischen Sprache und DOM gab), wird die Variante mit Funktionsausdruck/-referenz spätestens seit Mozilla 1.0 und Internet Explorer 5.5 unterstützt. (Das würdest Du unter anderem wissen, wenn Du unter anderem die ES Matrix auch mal *lesen* würdest.) Durch die String-Variante ergeben sich also gegenüber der Variante mit Funktionsausdruck/-referenz *keinerlei* Vorteile und *nur* Nachteile. > Und ja, die Problematik von eval() ist mir sehr wohl bewusst > und ja, wenn ich es mal umbaue, werde ich sicher eine andere Lösung > bevorzugen. Die Lösung ist trivial; ich hatte sie Dir sogar bereits hingeschrieben. Somit kann ich nur hoffen, dass ich mit diesem Posting nicht *wieder* meine kostbare Freizeit (an Dich) verschwendet habe. -- PointedEars FAQ: | SVN: Twitter: @PointedEars2 | ES Matrix: Please do not cc me. / Bitte keine Kopien per E-Mail.