Otimização de desempenho de aplicações JavaScript: um guia prático

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Otimização de desempenho de aplicações JavaScript: na prática



O desempenho de aplicações JavaScript não é apenas um bônus agradável, mas um fator criticamente importante que afeta a experiência do usuário (UX), a conversão e os custos de infraestrutura. Um carregamento lento de página ou um atraso na resposta da API pode custar até 40% do seu tráfego. Neste artigo, veremos métodos práticos de otimização de código JavaScript (no ambiente Node.js) que ajudarão você a tornar sua aplicação mais rápida e eficiente. Não vamos nos aprofundar nos fundamentos teóricos do Event Loop, mas sim passar diretamente para técnicas específicas com exemplos.



1. Cache: reduzindo a carga no servidor



Uma das maneiras mais eficazes de acelerar uma aplicação é evitar a repetição de operações caras. O cache permite salvar o resultado de um cálculo ou consulta ao banco de dados e fornecê-lo em uma solicitação repetida.



Cache in-memory usando Map

Para cenários simples, onde os dados não exigem lógica complexa de invalidação, o objeto embutido Map é ideal. Ele é mais rápido que Object e mais adequado para adicionar e remover chaves com frequência.



const cache = new Map();

async function fetchUserData(userId) { if (cache.has(userId)) { console.log('Retornando do cache'); return cache.get(userId); }

// Simulação de uma consulta cara ao BD const data = await new Promise((resolve) => { setTimeout(() => resolve({ id: userId, name: `User_${userId}` }), 1000); });

cache.set(userId, data); return data;}

// Primeira chamada (aguarda 1s)console.time('first');await fetchUserData(1);console.timeEnd('first');

// Segunda chamada (instantânea)console.time('second');await fetchUserData(1);console.timeEnd('second');


Cache TTL (Time-To-Live)

Para que o cache não fique desatualizado, é necessário adicionar um tempo de vida ao registro. Vamos implementar um cache TTL simples.



class TTLMap {  constructor(ttlMs = 60000) {    this.ttl = ttlMs;    this.store = new Map();  }

set(key, value) { const record = { value, expiry: Date.now() + this.ttl }; this.store.set(key, record); }

get(key) { const record = this.store.get(key); if (!record) return null; if (Date.now() > record.expiry) { this.store.delete(key); return null; } return record.value; }}

const userCache = new TTLMap(5000); // cache por 5 segundos


2. Assincronia e otimização do Event Loop



Node.js é single-threaded, mas sua natureza assíncrona permite processar várias solicitações de forma eficiente. O principal inimigo do desempenho são as operações síncronas que bloqueiam o Event Loop.



Evite versões síncronas de funções

Os módulos fs, crypto e outros possuem equivalentes síncronos (readFileSync, crypto.pbkdf2Sync). Use-os apenas durante a inicialização da aplicação. Dentro dos manipuladores de solicitações, use apenas as versões assíncronas.



// RUIM: Bloqueia o Event Loopconst data = fs.readFileSync('/path/to/file.txt', 'utf-8');

// BOM: Assíncronoconst data = await fs.promises.readFile('/path/to/file.txt', 'utf-8');


Divida tarefas pesadas com setImmediate

Se você tem um loop síncrono pesado (por exemplo, processar um array de 10 milhões de elementos), ele bloqueará o servidor por vários segundos. Divida a tarefa em partes e use setImmediate para dar ao Event Loop a chance de processar outras solicitações entre as partes.



function processLargeArray(items, chunkSize = 1000) {  let index = 0;

function processChunk() { const chunk = items.slice(index, index + chunkSize); // Processa o pedaço chunk.forEach(item => heavyOperation(item));

index += chunkSize; if (index < items.length) { // Continua na próxima iteração do Event Loop

Blogs

Book Recommendations