Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

MessageChannel no Node.js: Guia Prático

Atualizado em: 15 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

O MessageChannel no Node.js cria dois canais conectados para troca de mensagens entre partes da aplicação. Cada extremidade é representada por um MessagePort, permitindo enviar objetos, buffers e outros valores usando o algoritmo de clonagem estruturada.

Essa API é especialmente útil com Worker Threads, arquiteturas baseadas em atores, filas internas e componentes que precisam conversar sem compartilhar diretamente todo o estado. Também é possível transferir uma porta para outro worker, criando canais dedicados entre threads.

Neste guia, você aprenderá a criar canais, enviar e receber mensagens, transferir ArrayBuffer e MessagePort, tratar erros, fechar recursos, implementar request-response, aplicar timeouts, controlar filas e evitar vazamentos.

O que é MessageChannel?

MessageChannel cria duas portas ligadas entre si. A documentação oficial de MessageChannel no Node.js descreve a API. O guia de MessageChannel na MDN apresenta o modelo compartilhado com navegadores.

Para trabalho paralelo, consulte Worker Threads no Node.js. Para objetos binários, veja Buffer no Node.js. O artigo sobre Event Loop no Node.js ajuda a entender quando callbacks de mensagem são executados.

Criando um canal

const {
  MessageChannel
} = require('node:worker_threads');

const { port1, port2 } = new MessageChannel();

Mensagens enviadas por port1 chegam a port2, e vice-versa.

Enviando uma mensagem

port2.on('message', message => {
  console.log('Recebido:', message);
});

port1.postMessage({
  type: 'greeting',
  text: 'Olá'
});

Objetos são clonados. O receptor não recebe a mesma referência original.

start()

Ao usar on('message'), a porta normalmente é iniciada automaticamente. Em integrações que usam outras interfaces, start() pode ser necessário:

port2.start();

Consulte o comportamento da versão usada.

Recebendo uma única mensagem

port2.once('message', message => {
  console.log(message);
});

once() evita manter listener quando apenas uma resposta é esperada.

Mensagens suportadas

O mecanismo aceita muitos tipos compatíveis com clonagem estruturada, incluindo:

  • objetos e arrays;
  • Map e Set;
  • Date e RegExp;
  • ArrayBuffer e TypedArray;
  • erros, conforme versão;
  • MessagePort transferível;
  • Blob em versões compatíveis.

Funções e alguns objetos nativos não podem ser clonados.

Clonagem e referências

const original = { count: 1 };
port1.postMessage(original);
original.count = 2;

O receptor recebe uma cópia lógica com o valor no momento do envio. Não use mensagens como memória compartilhada.

Transferindo ArrayBuffer

const buffer = new ArrayBuffer(1024);

port1.postMessage(
  { buffer },
  [buffer]
);

Ao incluir o ArrayBuffer na transfer list, a propriedade é movida sem cópia. O buffer original fica destacado e não deve mais ser usado.

Buffer e pool interno

Nem todo Buffer deve ser transferido diretamente. Buffers criados a partir de pools podem compartilhar ArrayBuffer com outros dados. Prefira ArrayBuffer dedicado ou faça uma cópia controlada.

SharedArrayBuffer

SharedArrayBuffer não é transferido; ele permanece compartilhado entre threads. Isso exige sincronização com Atomics e um protocolo rigoroso.

Transferindo uma porta

worker.postMessage(
  { port: port2 },
  [port2]
);

Após a transferência, a origem não deve continuar usando a porta. O worker pode conversar diretamente com port1.

Canal dedicado entre workers

O processo principal pode criar um MessageChannel e entregar uma extremidade para cada worker. Assim, eles trocam mensagens sem encaminhar tudo pela thread principal.

Request-response

Inclua identificador em cada pedido:

const pending = new Map();
let sequence = 0;

function request(payload) {
  const id = ++sequence;

  return new Promise((resolve, reject) => {
    pending.set(id, { resolve, reject });
    port1.postMessage({ id, payload });
  });
}

Processando respostas

port1.on('message', message => {
  const operation = pending.get(message.id);
  if (!operation) return;

  pending.delete(message.id);

  if (message.ok) {
    operation.resolve(message.value);
  } else {
    operation.reject(new Error(message.error));
  }
});

Valide a estrutura da mensagem antes de acessar propriedades.

Timeout de resposta

function requestWithTimeout(payload, timeout = 5000) {
  const id = ++sequence;

  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      pending.delete(id);
      reject(new Error('Tempo limite excedido'));
    }, timeout);

    pending.set(id, {
      resolve(value) {
        clearTimeout(timer);
        resolve(value);
      },
      reject(error) {
        clearTimeout(timer);
        reject(error);
      }
    });

    port1.postMessage({ id, payload });
  });
}

Sem timeout, o Map pode crescer indefinidamente se o receptor falhar.

Cancelamento

Envie uma mensagem de cancelamento com o mesmo ID. O receptor precisa cooperar e interromper o trabalho quando possível.

Veja AbortController no Node.js para sinais e prazos.

Erros de mensagem

port1.on('messageerror', error => {
  console.error('Falha ao desserializar', error);
});

O evento ajuda a detectar valores que não puderam ser desserializados.

Fechando portas

port1.close();
port2.close();

Feche quando o canal não for mais necessário. Remova listeners e rejeite operações pendentes.

Evento close

port1.on('close', () => {
  for (const operation of pending.values()) {
    operation.reject(new Error('Canal fechado'));
  }
  pending.clear();
});

ref() e unref()

Uma porta ativa pode manter o processo vivo. Em versões compatíveis, use unref() quando o canal não deve impedir o encerramento:

port1.unref();

Use ref() para restaurar o comportamento.

Backpressure

postMessage() não fornece o mesmo sinal de backpressure de uma stream. Um produtor rápido pode gerar muitas mensagens e aumentar memória.

Implemente limite de solicitações pendentes, janela de crédito ou confirmações.

Protocolo com créditos

O consumidor informa quantas mensagens pode receber. O produtor envia até o limite e aguarda novos créditos. Esse padrão evita filas ilimitadas.

Tamanho das mensagens

Prefira mensagens pequenas e transfira buffers grandes. Clonar objetos profundos consome CPU e memória.

Validação

function isRequest(message) {
  return message
    && Number.isInteger(message.id)
    && typeof message.type === 'string';
}

Mesmo entre componentes internos, mensagens podem chegar fora de ordem ou com versão incompatível.

Versionamento do protocolo

{
  version: 1,
  type: 'resize-image',
  id: 42,
  payload: {}
}

Versionar facilita atualizações graduais de workers.

Segurança

MessageChannel não cria isolamento de segurança por si só. Um worker com acesso ao processo pode consumir CPU, memória e recursos permitidos.

Use o Permission Model no Node.js e controles do sistema como defesa adicional.

Observabilidade

Registre:

  • tipo da mensagem;
  • ID;
  • tempo de resposta;
  • fila pendente;
  • timeouts;
  • erros de serialização;
  • canal fechado.

Não registre payloads sensíveis.

Testes

Cubra:

  • mensagem simples;
  • resposta correlacionada;
  • timeout;
  • fechamento;
  • transferência de ArrayBuffer;
  • transferência de porta;
  • mensagem inválida;
  • cancelamento;
  • fila máxima;
  • worker encerrado.

Use o Node Test Runner.

Erros comuns

  • Não fechar portas: o processo permanece vivo.
  • Sem timeout: Promises pendentes acumulam.
  • Transferir buffer compartilhado: dados inesperados podem ser afetados.
  • Enviar objetos gigantes: clonagem fica cara.
  • Sem limite de fila: memória cresce.
  • Não validar mensagens: protocolo quebra silenciosamente.
  • Usar como sandbox: o canal não limita capacidades.

Boas práticas

  • Defina um protocolo versionado.
  • Use IDs de correlação.
  • Aplique timeouts.
  • Limite operações pendentes.
  • Transfira buffers grandes.
  • Valide mensagens.
  • Feche portas.
  • Rejeite pedidos no close.
  • Monitore filas.
  • Teste falhas de worker.

Conclusão

O MessageChannel no Node.js oferece comunicação estruturada entre componentes e Worker Threads por meio de duas MessagePorts conectadas.

O recurso funciona melhor com protocolo explícito, IDs, timeouts, limites e limpeza de recursos. Ao transferir buffers e portas de forma controlada, aplicações conseguem distribuir trabalho entre threads sem depender de estado global compartilhado ou criar filas internas ilimitadas.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita