Convertendo CSV gigantes para JSON sem travar a aba
Streams, Web Workers e um truque de chunking que mantém o conversor responsivo mesmo com 200MB de dados.
Recebi um CSV de 180MB de dados de transações financeiras e precisava convertê-lo para JSON para processar em uma pipeline. A primeira abordagem travou a aba depois de uns 20 segundos. A segunda também. A terceira funcionou, e é o que vou descrever aqui.
O problema com conversores ingênuos não é o algoritmo — é bloquear a main thread enquanto processa.
Por que a aba trava
Quando você faz FileReader.readAsText(file) e processa tudo em memória na main thread, você está competindo com o loop de eventos do navegador. Para arquivos grandes, esse processamento dura tempo suficiente para o browser parar de responder a inputs e animações.
A solução tem dois eixos: chunking (processar em pedaços) e Web Workers (processar fora da main thread).
Chunking com FileReader
Em vez de ler o arquivo inteiro de uma vez, leia em pedaços:
const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB por vez
let offset = 0;
function readNextChunk() {
const slice = file.slice(offset, offset + CHUNK_SIZE);
const reader = new FileReader();
reader.onload = (e) => {
processChunk(e.target.result);
offset += CHUNK_SIZE;
if (offset < file.size) {
setTimeout(readNextChunk, 0); // cede o controle ao loop de eventos
} else {
finalize();
}
};
reader.readAsText(slice);
}
O setTimeout(readNextChunk, 0) é o truque. Ele não espera nenhum tempo — mas cede o controle ao loop de eventos do browser antes de processar o próximo chunk. Isso mantém a UI responsiva.
Movendo para um Web Worker
Chunking resolve a responsividade, mas o processamento ainda acontece na main thread. Para arquivos acima de ~50MB, mova tudo para um Worker:
// main.js
const worker = new Worker('/csv-worker.js');
worker.postMessage({ file }); // File é transferível
worker.onmessage = (e) => {
if (e.data.type === 'progress') updateProgress(e.data.percent);
if (e.data.type === 'done') handleResult(e.data.json);
};
// csv-worker.js
self.onmessage = async (e) => {
const { file } = e.data;
const text = await file.text();
const json = parseCSV(text);
self.postMessage({ type: 'done', json });
};
Com isso, o parsing acontece em uma thread separada e a main thread nunca bloqueia, independente do tamanho do arquivo.
O problema do header
Chunking tem um efeito colateral: a primeira linha do CSV (o header com os nomes das colunas) precisa ser processada antes de qualquer linha de dados. Guarde os headers do primeiro chunk e use-os para montar os objetos de todos os chunks seguintes:
let headers = null;
function processChunk(text) {
const lines = text.split('\n');
if (!headers) {
headers = lines.shift().split(',');
}
for (const line of lines) {
if (!line.trim()) continue;
const values = line.split(',');
result.push(Object.fromEntries(headers.map((h, i) => [h, values[i]])));
}
}
Atenção: este parser simples não lida com campos que contêm vírgulas entre aspas. Para CSVs com esse padrão, use uma biblioteca como papaparse, que tem suporte nativo a streaming e Web Workers.
O CSV para JSON do Quick Tools
O conversor usa exatamente essa abordagem: chunking + processamento assíncrono, com progresso visual. Para arquivos acima de 10MB, ele automaticamente move o parsing para um Worker. O resultado é exportável como JSON formatado ou minificado.
Para dados financeiros sensíveis que você não quer enviar para um servidor, essa abordagem client-side é a única opção correta.
Como decidir entre converter tudo ou processar em stream
A diferença prática entre uma conversão simples e uma conversão em stream aparece quando o arquivo deixa de caber confortavelmente na memória. Para arquivos pequenos, converter CSV para JSON de uma vez é mais simples e suficiente. Para arquivos grandes, o ideal é ler linha por linha, validar cada registro e escrever a saída progressivamente.
Use processamento em stream quando o CSV vem de exportações operacionais, relatórios financeiros, logs ou integrações com milhares de linhas. Esse modelo permite detectar erros cedo, exibir progresso e evitar travar o navegador ou o processo Node. Também facilita descartar linhas inválidas sem perder o arquivo inteiro.
Checklist de validação
Antes de confiar no JSON final, confira encoding, delimitador, cabeçalhos duplicados, colunas vazias e quebras de linha dentro de aspas. Esses são os casos que mais quebram parsers ingênuos. Depois, valide tipos: números como string, datas em formatos mistos e valores booleanos escritos como sim, não, true, false, 1 ou 0.
Para arquivos pequenos, o CSV to JSON resolve rapidamente a inspeção visual. Para pipelines recorrentes, use a ferramenta para entender o formato e depois replique a regra em código com testes cobrindo linhas problemáticas.
- 01 diff antes de abrir PR: o hábito que reduz comentários de revisão Ler o próprio diff antes de abrir um PR muda o que você vê no código. Como e por que esse hábito reduz ciclos de revisão e melhora a qualidade dos PRs.
- 02 `P@ssw0rd1` passa na sua validação. Esse é o problema. O que o NIST mudou em 2024 sobre senhas: comprimento bate complexidade, leet speak está no rockyou.txt e rotação obrigatória enfraquece senhas.