Perfilamento de código TypeScript: encontrando gargalos na prática
Quando seu projeto TypeScript começa a ficar lento e os usuários reclamam de atrasos, chega a hora do perfilamento. Perfilamento não é apenas "ver o que está lento", mas uma abordagem sistemática para identificar gargalos no desempenho do código. Neste artigo, vamos explorar métodos práticos de perfilamento de código TypeScript com exemplos reais e ferramentas.
Por que perfilhar código TypeScript?
TypeScript é compilado para JavaScript, e todos os problemas de desempenho inerentes ao JS são transferidos para o TS. No entanto, o TypeScript adiciona suas próprias nuances: tipagem, decoradores, genéricos podem criar custos indiretos inesperados. O perfilamento ajuda a responder perguntas como:
- Qual função está demorando mais para executar?
- Onde está ocorrendo um vazamento de memória?
- Por que a animação está travando em 60fps?
- Qual algoritmo precisa de otimização?
Ferramentas para perfilamento TypeScript
Chrome DevTools (para código de navegador)
A ferramenta mais poderosa para frontend. Abra a aba Performance, clique em Record, execute a ação e pare a gravação. Você verá um flamegraph — uma visualização das chamadas de função e seu tempo de execução.
// Exemplo de código lento para perfilamentofunction heavyCalculation(iterations: number): number { let result = 0; for (let i = 0; i < iterations; i++) { result += Math.sqrt(i) * Math.sin(i); } return result;}
// Execute no console do navegador e depois perfilheheavyCalculation(1000000);Node.js — perfilador integrado
Para código de servidor, use a flag --inspect e o Chrome DevTools, ou o --prof integrado para gerar logs.
// server.tsimport express from 'express';
const app = express();
app.get('/heavy', (req, res) => { const data = processData(10000); res.json({ result: data });});
function processData(size: number): number[] { const arr: number[] = []; for (let i = 0; i < size; i++) { arr.push(i * Math.random()); } return arr.sort((a, b) => a - b);}
app.listen(3000);// Execução: node --prof dist/server.jsBenchmark.js para medições precisas
Quando precisar comparar duas implementações, use a biblioteca benchmark.
import Benchmark from 'benchmark';
const suite = new Benchmark.Suite();
// Versão lentafunction slowSearch(arr: number[], target: number): boolean { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) return true; } return false;}
// Versão rápida com Setfunction fastSearch(arr: number[], target: number): boolean { const set = new Set(arr); return set.has(target);}
const testArray = Array.from({ length: 10000 }, (_, i) => i);
suite .add('slowSearch', () => slowSearch(testArray, 9999)) .add('fastSearch', () => fastSearch(testArray, 9999)) .on('cycle', (event: any) => console.log(String(event.target))) .on('complete', function(this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')); }) .run({ async: true });Exemplos práticos de localização de gargalos
Exemplo 1: Vazamento de memória através de closures
Um problema típico em aplicações React — closures que mantêm referências a objetos grandes.
// Código problemáticofunction createHeavyComponent() { const hugeData = new Array(1000000).fill('data'); return { getData: () => hugeData, // closure mantém hugeData updateData: (newData: string) => { // mutação, mas a referência permanece hugeData.push(newData); } };}
const component = createHeavyComponent();// hugeData não será coletado pelo GC enquanto component existir<