Seu site está lento? Hora de fazer profiling do JavaScript
\nVocê já sentiu aquela dor: o site carrega, as animações são suaves, mas quando o usuário começa a interagir ativamente com a página — tudo trava. Os botões não respondem, a rolagem fica irregular e o console está em silêncio. Familiar? Provavelmente, o problema está no desempenho do JavaScript. Esta linguagem é a alma da web moderna, mas sem a otimização adequada, ela pode transformar sua obra-prima em um monstro lento.
\nNeste artigo, não vamos dar palestras chatas. Vamos pegar um código real, analisá-lo minuciosamente com profiling e mostrar como extrair o máximo de cada ciclo. Pronto para transformar seu site em um carro de Fórmula 1? Vamos nessa!
\n\nPor que o JavaScript fica lento: a raiz do problema
\nAntes de pegar as ferramentas, vamos entender o que exatamente rouba o desempenho. Na maioria das vezes, três coisas são culpadas:
\n- \n
- Bloqueio da thread principal — o JavaScript funciona em uma única thread (Event Loop). Se você executar uma operação síncrona pesada, a interface congelará até sua conclusão. \n
- Vazamentos de memória — manipuladores de eventos esquecidos, referências fechadas e variáveis globais. A memória cresce, o site fica mais lento e depois trava. \n
- Algoritmos ineficientes — loops duplos em arrays enormes, manipulações frequentes do DOM, estruturas de dados não otimizadas. \n
Mas chega de teoria. Vamos ver como isso se parece na prática.
\n\nFerramentas de profiling: seu detector de mentiras
\nChrome DevTools — o canivete suíço do profiling
\nAbra qualquer página, pressione F12 e vá para a aba Performance. Pressione o botão de gravação, execute uma ação na página (por exemplo, clique em um botão) e pare a gravação. Você verá uma linha do tempo onde cada segundo é detalhado por frames. As barras vermelhas são seus inimigos. Elas significam que o navegador não conseguiu renderizar o frame em 16,6 ms (60 FPS).
\n// Exemplo: uma função simples, mas problemática\nfunction heavyCalculation() {\n let result = 0;\n for (let i = 0; i < 100000000; i++) {\n result += Math.sqrt(i) * Math.sin(i);\n }\n return result;\n}\n\ndocument.getElementById('btn').addEventListener('click', () => {\n const start = performance.now();\n heavyCalculation(); // A interface congelará por 2-3 segundos!\n console.log('Tempo:', performance.now() - start);\n});\n\nExecute o profiling e clique no botão. Você verá que a thread principal está ocupada com a chamada heavyCalculation por um segundo inteiro. Isso é o bottleneck.
Memory — caça aos vazamentos
\nVá para a aba Memory e tire um snapshot do heap. Em seguida, execute uma ação na página várias vezes e tire um segundo snapshot. Compare-os. Se a quantidade de objetos de um mesmo tipo crescer sem parar — você tem um vazamento.
\n// Vazamento clássico: manipulador esquecido\nfunction createButton() {\n const btn = document.createElement('button');\n btn.textContent = 'Clique em mim';\n document.body.appendChild(btn);\n \n // O manipulador mantém uma referência ao btn, mesmo se btn for removido\n btn.addEventListener('click', function onClick() {\n console.log('Clicado');\n });\n \n // Remove o botão, mas o manipulador permanece na memória\n document.body.removeChild(btn);\n}\n\n\nTécnicas de otimização: da teoria à prática
\n1. Desafogando a thread principal com async/await
\nSe você tem uma tarefa pesada, não bloqueie a thread. Use Web Workers ou divida a tarefa em microtarefas. Mas a maneira mais simples é setTimeout com atraso zero ou requestIdleCallback.
// Ruim: carregamento síncrono\nfunction loadBigData() {\n const data = [];\n for (let i = 0; i < 1000000; i++) {\n data.push({ id: i, value: Math.random() });\n }\n render(data);