A API BroadcastChannel permite que scripts de mesma origem enviem mensagens para outros contextos de navegação. Ele pode ser considerado um barramento de mensagens simples que permite a semântica de pub/sub entre janelas/abas, iframes, workers da Web e workers de serviço.
Princípios básicos da API
A API Broadcast Channel é uma API simples que facilita a comunicação entre contextos de navegação. Ou seja, a comunicação entre janelas/abas, iframes, workers da Web e workers de serviço. As mensagens postadas em um determinado canal são entregues a todos os listeners desse canal.
O construtor BroadcastChannel
usa um único parâmetro: o nome de um canal.
O nome identifica o canal e é exibido em todos os contextos de navegação.
// Connect to the channel named "my_bus".
const channel = new BroadcastChannel('my_bus');
// Send a message on "my_bus".
channel.postMessage('This is a test message.');
// Listen for messages on "my_bus".
channel.onmessage = function(e) {
console.log('Received', e.data);
};
// Close the channel when you're done.
channel.close();
Como enviar mensagens
As mensagens podem ser strings ou qualquer coisa com suporte ao algoritmo de clone estruturado (strings, objetos, matrizes, blobs, ArrayBuffer, mapa).
Exemplo: envio de um Blob ou arquivo
channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));
Um canal não transmite para si mesmo. Portanto, se você tiver um listener onmessage
na mesma página que um postMessage()
para o mesmo canal, esse evento message
não será acionado.
Diferenças em relação a outras técnicas
Neste ponto, você pode estar se perguntando como isso se relaciona a outras técnicas de transmissão de mensagens, como WebSockets, SharedWorkers, a API MessageChannel
e window.postMessage()
. A API Broadcast Channel não substitui essas APIs. Cada um tem uma finalidade. A API Broadcast Channel foi criada para facilitar a comunicação de um para muitos entre scripts na mesma origem.
Alguns casos de uso para canais de transmissão:
- Detectar ações do usuário em outras guias
- Saber quando um usuário faz login em uma conta em outra janela/guia.
- Instruir um worker a realizar algum trabalho em segundo plano
- Saber quando um serviço terminou de realizar uma ação.
- Quando o usuário fizer o upload de uma foto em uma janela, transmita-a para outras páginas abertas.
Exemplo: página que sabe quando o usuário faz logout, mesmo em outra guia aberta no mesmo site:
<button id="logout">Logout</button>
<script>
function doLogout() {
// update the UI login state for this page.
}
const authChannel = new BroadcastChannel('auth');
const button = document.querySelector('#logout');
button.addEventListener('click', e => {
// A channel won't broadcast to itself so we invoke doLogout()
// manually on this page.
doLogout();
authChannel.postMessage({cmd: 'logout', user: 'Eric Bidelman'});
});
authChannel.onmessage = function(e) {
if (e.data.cmd === 'logout') {
doLogout();
}
};
</script>
Em outro exemplo, digamos que você queira instruir um worker de serviço para remover
o conteúdo armazenado em cache depois que o usuário mudar a "configuração de armazenamento off-line" no app.
É possível excluir os caches usando window.caches
, mas o worker de serviço pode
já conter um utilitário para fazer isso. Podemos usar a API Broadcast Channel para
reutilizar esse código. Sem a API Broadcast Channel, você precisaria fazer um loop sobre os resultados de self.clients.matchAll()
e chamar postMessage()
em cada cliente para conseguir a comunicação de um worker de serviço para todos os clientes (código real que faz isso). O uso de um canal de transmissão faz com que isso seja O(1)
em vez de O(N)
.
Exemplo: instrua um service worker a remover um cache, reutilizando os métodos de utilitário internos.
Em index.html
const channel = new BroadcastChannel('app-channel');
channel.onmessage = function(e) {
if (e.data.action === 'clearcache') {
console.log('Cache removed:', e.data.removed);
}
};
const messageChannel = new MessageChannel();
// Send the service worker a message to clear the cache.
// We can't use a BroadcastChannel for this because the
// service worker may need to be woken up. MessageChannels do that.
navigator.serviceWorker.controller.postMessage({
action: 'clearcache',
cacheName: 'v1-cache'
}, [messageChannel.port2]);
Em sw.js
function nukeCache(cacheName) {
return caches.delete(cacheName).then(removed => {
// ...do more stuff (internal) to this service worker...
return removed;
});
}
self.onmessage = function(e) {
const action = e.data.action;
const cacheName = e.data.cacheName;
if (action === 'clearcache') {
nukeCache(cacheName).then(removed => {
// Send the main page a response via the BroadcastChannel API.
// We could also use e.ports[0].postMessage(), but the benefit
// of responding with the BroadcastChannel API is that other
// subscribers may be listening.
const channel = new BroadcastChannel('app-channel');
channel.postMessage({action, removed});
});
}
};
Diferença com postMessage()
Ao contrário de postMessage()
, você não precisa mais manter uma referência a um iframe ou worker para se comunicar com ele:
// Don't have to save references to window objects.
const popup = window.open('https://another-origin.com', ...);
popup.postMessage('Sup popup!', 'https://another-origin.com');
O window.postMessage()
também permite a comunicação entre origens. A API Broadcast Channel é de mesma origem. Como as mensagens têm garantia de origem, não é necessário validá-las como antes com window.postMessage()
:
// Don't have to validate the origin of a message.
const iframe = document.querySelector('iframe');
iframe.contentWindow.onmessage = function(e) {
if (e.origin !== 'https://expected-origin.com') {
return;
}
e.source.postMessage('Ack!', e.origin);
};
Basta "assinar" um canal específico e ter uma comunicação segura e bidirecional.
Diferença com SharedWorkers
Use BroadcastChannel
para casos simples em que você precisa enviar mensagens para várias janelas/guias ou workers.
Para casos de uso mais sofisticados, como gerenciar bloqueios, estado compartilhado, sincronizar recursos entre um servidor e vários clientes ou compartilhar uma conexão WebSocket com um host remoto, os workers compartilhados são a solução mais adequada.
Diferença com a API MessageChannel
A principal diferença entre a API Channel Messaging e o BroadcastChannel
é que o último é um meio de enviar mensagens para vários listeners (um para muitos). MessageChannel
é destinado à comunicação direta entre scripts. Ele também é mais complexo, exigindo a configuração de canais com uma porta em cada extremidade.
Detecção de recursos e suporte a navegadores
Atualmente, o Chrome 54, o Firefox 38 e o Opera 41 são compatíveis com a API Broadcast Channel.
if ('BroadcastChannel' in self) {
// BroadcastChannel API supported!
}
Quanto aos polyfills, há alguns disponíveis:
- https://gist.github.com/alexis89x/041a8e20a9193f3c47fb
- https://gist.github.com/inexorabletash/52f437d1451d12145264
Eu não testei esses métodos, então sua experiência pode variar.