Chyba přetečení zásobníku JavaScriptu

Čas od času jsem blogoval o limitech prohlížeče JavaScript a o tom, jak se prezentují. Začal jsem tím, že jsem diskutoval o dlouhotrvajícím dialogu skriptu a poté jsem přešel k dalším problémům s výkonem. Myslel jsem, že jsem pokryl většinu nepříjemných a špatně vysvětlených limitů JavaScriptu, ale minulý týden jsem narazil na další, o kterém stojí za to diskutovat:chyby přetečení zásobníku.

Psal jsem o tom, jak příliš mnoho rekurze může vést k problémům s výkonem. Většina prohlížečů má omezení povolené rekurze, než se skript automaticky zruší. Toto je limit, který je oddělený od limitu určujícího, zda je skript dlouhodobý. A limit není ani tak o rekurzivních voláních, jako spíše o velikosti zásobníku volání JavaScriptu.

Není divu, že různé prohlížeče mají různé velikosti zásobníku volání. Není také překvapením, že se také liší metoda, kterou používají k určení zásobníku volání. Různé velikosti zásobníku hovorů, které jsem mohl změřit, jsou (dát nebo přijmout, může být mimo o 1 nebo 2):

  • Internet Explorer 7:1 789
  • Firefox 3:3 000
  • Chrome 1:21 837
  • Opera 9.62:10 000
  • Safari 3.2:500

Někteří uvedli, ale nemohu potvrdit, že velikost zásobníku volání IE a Opery je poněkud svázána s množstvím paměti RAM v systému. Všechny ostatní prohlížeče mají toto výchozí nastavení. Za zmínku také stojí, že WebKit má podle všeho mnohem vyšší limit a že Safari zavádí přísnější limit pro JavaScript engine.

Existují dva běžné scénáře, ve kterých lze tohoto limitu dosáhnout. První je jednoduchá rekurze, například:

function recurse(){
    recurse();
}

recurse();

Druhý je složitější a hůře identifikovatelný problém, zejména ve velkých kódových bázích, kde dvě funkce každá volají druhou, jako například:

function doSomething(){
    doSomethingElse();
}

function doSomethingElse(){
    doSomething();
}

doSomething();

V každém případě prohlížeč zastaví váš kód a (doufejme) zobrazí zprávu o problému:

  • Internet Explorer 7:„Přetečení zásobníku na řádku x“
  • Firefox 3:„Příliš mnoho rekurze“
  • Chrome 1:n/a
  • Opera 9.62:„Přerušit (přetečení zásobníku řízení)“
  • Safari 3.2:”RangeError:Překročena maximální velikost zásobníku volání.”

Chrome je jediný prohlížeč, který nezobrazuje zprávu o problému. Pokud se zobrazí některá z těchto chybových zpráv, znamená to, že se jedná o jeden ze dvou vzorů a je třeba jej změnit. S touto chybou je obvykle spojeno číslo řádku a název souboru, takže je poměrně snadné ladit.

Nejzajímavější částí chyb přetečení zásobníku je, že se v některých prohlížečích jedná o skutečné chyby JavaScriptu, a proto je lze zachytit pomocí try-catch tvrzení. Typ výjimky se liší v závislosti na používaném prohlížeči. Ve Firefoxu je to InternalError , v Safari a Chrome je to RangeError a Internet Explorer vyvolá obecný Error typu (Opera nevyhodí chybu, pouze zastaví JavaScript engine) . Takže je možné udělat něco takového:

try {
    recurse();
} catch (ex){
    alert("Too much recursion!");
}

Pokud tyto chyby zůstanou bez pasti, budou se tyto chyby objevovat jako jakákoli jiná chyba (ve Firefoxu skončí v konzole Firebug, Safari/Chrome se zobrazí v konzoli) kromě Internet Exploreru. IE nejenže zobrazí chybu JavaScriptu, ale také zobrazí ošklivé dialogové okno, které vypadá jako upozornění se zprávou o přetečení zásobníku.

To, že je možné tuto chybu zachytit téměř ve všech prohlížečích, neznamená, že byste měli. Žádný kód by neměl skončit ve výrobě, i když existuje možnost chyby přetečení zásobníku. Takové případy naznačují špatný návrh kódu a měly by být přehodnoceny a/nebo přepracovány, aby se zabránilo této chybě. Považujte tento příspěvek za pomůcku při ladění tohoto problému, nikoli za licenci k jeho zachycení a zamaskování.