
Toda explicação desse erro diz a mesma coisa verdadeira e inútil: uma função chamou a si mesma vezes demais, então acrescente um caso base.
Isso você já sabia. O que você quer saber é qual chamada começou tudo, e esse é o único erro do JavaScript cujo stack trace estruturalmente não consegue dizer.
Tudo o que foi medido abaixo rodou no Chrome. Os números são do V8; a forma vale em qualquer motor que capture uma pilha no momento em que o erro é lançado.
O que o trace contém de fato
Pegue uma cadeia em que uma função chama outra, que chama uma terceira, que começa a recorrer, e capture o erro:
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, e então dez linhas, cada uma delas 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 não está ali. Nem level1, nem level2. O trace nomeia dez vezes a função que você já
conhecia, e mais nada.
Dez, porque Error.stackTraceLimit vale 10 por padrão.
Aumentar o limite não ajuda, e essa é a parte que vale saber
O movimento óbvio é pedir mais frames:
Error.stackTraceLimit = 200;
Faça isso e você recebe 200 linhas. Medido na cadeia acima, todas as 200 são recurse, e
entryPoint continua ausente.
A razão está em de onde o trace é capturado. Ele é tirado no momento do lançamento, que é o ponto mais profundo da pilha, e caminha de volta a partir dali. Seu ponto de entrada está na outra ponta, a milhares de frames. Pedir 200 frames traz 200 frames da ponta errada.
É por isso que aqui falha “leia o stack trace de baixo para cima”, que é a instrução certa para
quase qualquer outro erro. O fundo desse trace também é recurse.
Capture cedo, e a janela é estreita
A informação que você quer existe. Ela só está disponível enquanto a pilha ainda é rasa.
Então capture um trace de dentro da função que recorre, numa passagem inicial, e jogue o erro fora:
function recurse(n) {
if (n === 2) console.log(new Error("where did this start").stack);
return recurse(n + 1);
}
Na cadeia acima, esse trace termina assim:
at level2 (<anonymous>:4:27)
at level1 (<anonymous>:4:27)
at entryPoint (<anonymous>:3:31)
Que é a resposta. A cadeia inteira, em ordem, a partir da chamada que a iniciou.
A janela é mais ou menos tão larga quanto o seu limite de frames, e aí está a pegadinha.
Capturar na profundidade 2 dá o ponto de entrada. Capturar na profundidade 20, com o limite ainda
no padrão de 10, não dá, porque a essa altura os dez frames que o trace vai guardar já passaram do
começo. Então os dois movimentos funcionam juntos e nenhum funciona sozinho: aumente o
Error.stackTraceLimit e capture numa passagem inicial.
Se você não sabe qual função está recorrendo, o erro diz pelo menos isso. É o nome repetido dez vezes.
A versão que chega num chamado
Tudo acima pressupõe que você está no teclado enquanto acontece. Pense no que chega até você quando acontece com outra pessoa.
A página congelou, ou ficou em branco, ou um botão parou de responder. Se essa pessoa abriu o console e tirou uma foto, você tem dez linhas idênticas com o nome de uma função. Essa imagem não contém nenhuma informação sobre qual caminho do código rodou, que é a única coisa de que você precisa.
Isso faz do estouro de pilha um caso incomum entre os erros de navegador. Para a maioria das falhas o console é o artefato que vale pedir, e para esse ele é quase sem valor: dez cópias de um fato que você poderia ter adivinhado. O que de fato estreita o campo é a sequência de ações imediatamente antes do congelamento, porque o ponto de entrada é algo que a pessoa fez.
O que é uma inversão incômoda. O erro com mais saída no console é o erro em que a saída do console ajuda menos, e em que o que a pessoa estava fazendo importa mais.
Session Replay
Extensão gratuita para Chrome. Um clique na página que está com problema captura a tela, o console e o log de rede, e entrega um link para você colar no chamado.
O que levar disso
O erro é honesto e quase sem conteúdo: algo recorreu sem parar. Ele nomeia a função do fundo do laço e cala a do topo, não por mesquinharia, mas por causa do lugar de onde um stack trace é tirado.
Então pare de ler esse trace e vá buscar outro, de uma passagem inicial, com o limite aumentado. Ou, quando for o navegador de outra pessoa, pare de pedir o console e pergunte o que ela apertou.