postMessage: transfira, não copie
Um segundo argumento no postMessage evita copiar centenas de MB entre a página e o Web Worker.
![]()
- O projeto tá aberto: github.com/joaomarcostg/compacta-pdf
Quando você manda um objeto pra um Web Worker com postMessage, o navegador faz uma cópia estruturada dele. Pra um JSON pequeno, tanto faz. Pra um PDF de 266 MB, você acabou de alocar mais 266 MB e gastar tempo copiando byte por byte.
O postMessage aceita um segundo argumento: a lista de objetos transferíveis. Um ArrayBuffer nessa lista não é copiado. A posse dele passa pro outro lado.
No Compacta PDF, a página lê o arquivo e entrega o buffer pro worker assim:
const buffer = await job.file.arrayBuffer();
worker.postMessage(
{ type: 'compress', id: job.id, buffer, preset: job.preset },
[buffer],
);
Depois dessa linha, o buffer na página fica com byteLength igual a zero. Ele foi “desanexado”. Se você tentar ler, não tem nada lá. É o preço: um lado só pode usar a memória por vez.
Na volta é igual, com um cuidado a mais. O resultado chega como Uint8Array, e um Uint8Array pode ser só uma vista sobre um ArrayBuffer maior. O que se transfere é o buffer, não a vista. Se a vista cobrir só um pedaço, você manda o buffer inteiro junto. Então o worker recorta exatamente os bytes do arquivo antes de transferir:
const out = output.buffer.slice(
output.byteOffset,
output.byteOffset + output.byteLength,
);
postMessage({ type: 'done', id, buffer: out }, [out]);
O slice faz uma cópia, mas só do resultado, que já está compactado. É um seguro barato: funciona do mesmo jeito seja a vista do tamanho exato do buffer ou não.
Regra de bolso: se o que você está mandando pro worker é grande e você não vai mais usar do lado de cá, coloque na lista de transferíveis.