
Jede Erklärung dieses Fehlers sagt Ihnen dasselbe Wahre und Nutzlose: Eine Funktion hat sich zu oft selbst aufgerufen, also bauen Sie einen Abbruchfall ein.
Das wussten Sie. Was Sie wissen wollen, ist welcher Aufruf das angefangen hat, und das ist der eine Fehler in JavaScript, dessen Stacktrace es Ihnen strukturell nicht sagen kann.
Alles unten Gemessene lief in Chrome. Die Zahlen sind die von V8; die Form gilt in jeder Engine, die einen Stack im Moment des Werfens aufnimmt.
Was im Trace tatsächlich steht
Nehmen Sie eine Kette, in der eine Funktion eine andere aufruft, die eine dritte, die zu rekursieren beginnt, und fangen Sie den Fehler:
function entryPoint() { return level1() }
function level1() { return level2() }
function level2() { return recurse(0) }
function recurse(n) { return recurse(n + 1) }
try { entryPoint() } catch (e) { console.log(e.stack) }
RangeError: Maximum call stack size exceeded, und dann zehn Zeilen, jede einzelne recurse:
RangeError: Maximum call stack size exceeded
at recurse (<anonymous>:2:145)
at recurse (<anonymous>:2:156)
at recurse (<anonymous>:2:156)
... seven more identical lines
entryPoint steht nicht darin. level1 und level2 auch nicht. Der Trace nennt die Funktion,
von der Sie schon wussten, zehnmal, und sonst nichts.
Zehn, weil Error.stackTraceLimit standardmäßig 10 ist.
Das Limit zu erhöhen hilft nicht, und das ist der Teil, den man kennen sollte
Der naheliegende Griff ist, mehr Frames zu verlangen:
Error.stackTraceLimit = 200;
Tun Sie das, und Sie bekommen 200 Zeilen. An der Kette oben gemessen sind alle 200 recurse, und
entryPoint fehlt weiterhin.
Der Grund liegt darin, von wo der Trace aufgenommen wird. Er entsteht im Moment des Werfens, also am tiefsten Punkt des Stacks, und läuft von dort zurück. Ihr Einstiegspunkt liegt am anderen Ende, tausende Frames entfernt. 200 Frames zu verlangen bringt Ihnen 200 Frames vom falschen Ende.
Deshalb scheitert hier “den Stacktrace von unten nach oben lesen”, was bei
fast jedem anderen Fehler die richtige Anweisung ist. Das untere Ende dieses Traces ist
ebenfalls recurse.
Nehmen Sie ihn stattdessen früh auf, und das Fenster ist schmal
Die Information, die Sie wollen, existiert. Sie ist nur verfügbar, solange der Stack noch flach ist.
Nehmen Sie also einen Trace aus der rekursierenden Funktion heraus auf, bei einem frühen Durchlauf, und werfen Sie den Fehler weg:
function recurse(n) {
if (n === 2) console.log(new Error("where did this start").stack);
return recurse(n + 1);
}
An der Kette oben endet dieser Trace so:
at level2 (<anonymous>:4:27)
at level1 (<anonymous>:4:27)
at entryPoint (<anonymous>:3:31)
Und das ist die Antwort. Die ganze Kette, in der richtigen Reihenfolge, von dem Aufruf an, der sie begonnen hat.
Das Fenster ist etwa so breit wie Ihr Frame-Limit, und das ist der Haken. Bei Tiefe 2
aufgenommen liefert es den Einstiegspunkt. Bei Tiefe 20 aufgenommen, mit dem Limit noch auf den
voreingestellten 10, nicht mehr, denn bis dahin sind die zehn Frames, die der Trace halten wird,
alle über den Anfang hinausgeschoben. Die zwei Schritte wirken also zusammen und keiner wirkt
allein: Error.stackTraceLimit erhöhen, und bei einem frühen Durchlauf aufnehmen.
Wenn Sie nicht wissen, welche Funktion rekursiert, sagt der Fehler Ihnen immerhin das. Es ist der Name, der zehnmal dasteht.
Die Fassung, die als Ticket ankommt
Alles oben setzt voraus, dass Sie an der Tastatur sitzen, während es passiert. Überlegen Sie, was bei Ihnen ankommt, wenn es jemand anderem passiert.
Die Seite ist eingefroren, oder weiß geworden, oder ein Knopf reagiert nicht mehr. Wenn die Person die Konsole geöffnet und abfotografiert hat, haben Sie zehn identische Zeilen mit einem Funktionsnamen. Dieses Bild enthält keine Information darüber, welcher Codeweg gelaufen ist, und genau das ist das Einzige, was Sie brauchen.
Damit ist der Stacküberlauf unter den Browserfehlern ungewöhnlich. Bei den meisten Fehlern ist die Konsole das Artefakt, um das man bitten sollte, und bei diesem ist sie beinahe wertlos: zehn Kopien einer Tatsache, die man hätte erraten können. Was wirklich eingrenzt, ist die Folge der Handlungen unmittelbar vor dem Einfrieren, denn der Einstiegspunkt ist etwas, das die Person getan hat.
Das ist eine unbequeme Umkehrung. Der Fehler mit der meisten Konsolenausgabe ist der Fehler, bei dem Konsolenausgabe am wenigsten hilft und bei dem was die Person getan hat am meisten zählt.
Session Replay
Kostenlose Chrome-Erweiterung. Ein Klick auf die Seite, die Probleme macht, erfasst den Screenshot, die Konsole und das Netzwerkprotokoll und liefert Ihnen einen Link zum Einfügen in das Ticket.
Was davon zu behalten ist
Der Fehler ist ehrlich und fast inhaltsleer: Irgendetwas hat rekursiert, ohne aufzuhören. Er nennt die Funktion am unteren Ende der Schleife und verschweigt die am oberen, nicht aus Geiz, sondern wegen der Stelle, von der ein Stacktrace genommen wird.
Hören Sie also auf, den Trace zu lesen, und holen Sie sich einen anderen, aus einem frühen Durchlauf, mit erhöhtem Limit. Oder, wenn es der Browser eines anderen ist, hören Sie auf, nach der Konsole zu fragen, und fragen Sie, was die Person gedrückt hat.