Quando o navegador não é um brinquedo, mas um data center
\n\nEstamos acostumados que o JavaScript vive na sandbox do navegador: animações, cliques, formulários. Mas e se eu disser que com JS puro é possível processar um arquivo CSV de 500 MB, construir um gráfico com 100.000 pontos e não travar a aba? Isso não é fantasia, mas sim o uso inteligente do processamento de grandes dados em JavaScript.
\nNeste artigo, mostrarei como trabalhar com grandes volumes de dados diretamente no navegador — sem backend, sem Python, sem copiar gigabytes para o servidor. Você aprenderá sobre streaming, Web Workers, otimização de memória e bibliotecas que transformam o navegador em um mini processador de dados. Vamos lá!
\n\nPor que o processamento "ingênuo" de dados mata a performance
\nImagine: você carrega um arquivo CSV de 300 MB e escreve:
\nconst data = await response.json(); // ❌ Erro fatal\nO que acontece? O JS tenta analisar o arquivo inteiro na memória RAM. Se o usuário tiver 4 GB de RAM — o navegador trava. Mesmo em um PC potente, o coletor de lixo começa a "engasgar" e a interface congela por um minuto.
\nRegra nº1: não carregue tudo de uma vez. Grandes dados em JavaScript exigem processamento em fluxo (streaming) e divisão em chunks.
\n\nStreaming de dados: lendo o arquivo em pedaços
\nA API do navegador FileReader permite ler o arquivo em partes. Mas ainda mais conveniente é o ReadableStream. Exemplo: processamento de um grande arquivo JSON linha por linha.
Exemplo: streaming de JSON com NDJSON
\nO formato NDJSON (cada linha é um objeto JSON separado) é ideal para grandes dados. Veja como lê-lo em fluxo:
\nasync function* streamJSON(file) {\n const stream = file.stream();\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop(); // resto no buffer\n for (const line of lines) {\n if (line.trim()) yield JSON.parse(line);\n }\n }\n}\n\n// Uso\nfor await (const obj of streamJSON(selectedFile)) {\n processRow(obj); // processa um objeto por vez\n}\n\nA memória quase não cresce — mantemos apenas uma linha na memória. Essa abordagem é a base do processamento de grandes dados em JavaScript em projetos reais.
\n\nWeb Workers: não travando a interface
\nMesmo o streaming é executado na thread principal. Se os cálculos forem complexos — a UI congela. Solução: Web Workers. É uma thread separada onde é possível processar gigabytes sem lag.
\n\nCriando um Worker para processar CSV
\nCódigo do worker (worker.js):
\nself.onmessage = async function(e) {\n const { file, chunkSize } = e.data;\n const reader = new FileReaderSync(); // leitura síncrona — permitido no worker\n let offset = 0;\n const results = [];\n\n while (offset < file.size) {\n const blob = file.slice(offset, offset + chunkSize);\n const text = reader.readAsText(blob);\n const lines = text.split('\\n');\n for (const line of lines) {\n if (line.trim()) results.push(parseCSVLine(line));\n }\n offset += chunkSize;\n // Envia resultado intermediário\n self.postMessage({ type: 'chunk', data: results.splice(0, results.length) });\n }\n self.postMessage({ type: 'done' });\n};\n\nfunction parseCSVLine(line) {\n const [name, age, city] = line.split(',');\n return { name, age: parseInt(age), city };\n}\n\nThread principal:
\nconst worker = new Worker('worker.js');\nworker.postMessage({ file: csvFile, chunkSize: 1024 * 1024 }); // chunks de 1 MB\n\nworker.onmessage = (e) => {\n if (e.data.type === 'chunk') {\n updateUI(e.data.data); // atualiza tabela/gráfico suavemente\n }\n};\n\nAgora a interface permanece responsiva, mesmo processando 2 GB de dados. Essa é a técnica chave para b