La API de BroadcastChannel permite que las secuencias de comandos del mismo origen envíen mensajes a otros contextos de navegación. Se puede considerar como un bus de mensajes simple que permite la semántica de pub/sub entre ventanas o pestañas, iframes, trabajadores web y trabajadores de servicio.
Conceptos básicos de API
La API de Broadcast Channel es una API simple que facilita la comunicación entre los contextos de navegación. Es decir, la comunicación entre ventanas o pestañas, iframes, trabajadores web y trabajadores de servicio. Los mensajes que se publican en un canal determinado se entregan a todos los usuarios que lo escuchan.
El constructor BroadcastChannel
toma un solo parámetro: el nombre de un canal.
El nombre identifica el canal y se mantiene en todos los contextos de navegación.
// 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();
Cómo mandar mensajes
Los mensajes pueden ser cadenas o cualquier elemento compatible con el algoritmo de clonación estructurada (cadenas, objetos, arrays, BLOB, ArrayBuffer y Map).
Ejemplo: Envío de un BLOB o un archivo
channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));
Un canal no se transmitirá a sí mismo. Por lo tanto, si tienes un objeto de escucha onmessage
en la misma página que un postMessage()
en el mismo canal, no se activará ese evento message
.
Diferencias con otras técnicas
En este punto, es posible que te preguntes cómo se relaciona esto con otras técnicas para pasar mensajes, como WebSockets, SharedWorkers, la API de MessageChannel
y window.postMessage()
. La API de Broadcast Channel no reemplaza estas APIs. Cada uno tiene un propósito. La API de Broadcast Channel está diseñada para facilitar la comunicación de uno a varios entre secuencias de comandos en el mismo origen.
Estos son algunos casos de uso de los canales de transmisión:
- Cómo detectar acciones del usuario en otras pestañas
- Saber cuándo un usuario accede a una cuenta en otra ventana o pestaña
- Instruye a un trabajador para que realice un trabajo en segundo plano
- Saber cuándo un servicio termina de realizar alguna acción
- Cuando el usuario sube una foto en una ventana, pásala a otras páginas abiertas.
Ejemplo: Página que sabe cuándo el usuario sale, incluso desde otra pestaña abierta en el mismo sitio:
<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>
En otro ejemplo, supongamos que quieres indicarle a un trabajador de servicio que quite el contenido almacenado en caché después de que el usuario cambie su "configuración de almacenamiento sin conexión" en tu app. Podrías borrar sus cachés con window.caches
, pero es posible que el trabajador de servicio ya contenga una utilidad para hacerlo. Podemos usar la API de Broadcast Channel para
volver a usar ese código. Sin la API de Broadcast Channel, tendrías que repetir indefinidamente los resultados de self.clients.matchAll()
y llamar a postMessage()
en cada cliente para lograr la comunicación de un service worker con todos sus clientes (código real que lo hace). Si usas un canal de transmisión, este elemento será O(1)
en lugar de O(N)
.
Ejemplo: indica a un service worker que quite una caché y reutilice sus métodos de utilidad internos.
En 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]);
En 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});
});
}
};
Diferencia con postMessage()
A diferencia de postMessage()
, ya no tienes que mantener una referencia a un iframe o un trabajador para comunicarte con él:
// 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');
window.postMessage()
también te permite comunicarte entre orígenes. La API de Broadcast Channel es del mismo origen. Dado que se garantiza que los mensajes provengan del mismo origen, no es necesario validarlos como solíamos hacerlo con 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);
};
Simplemente, "suscríbete" a un canal en particular y disfruta de una comunicación segura y bidireccional.
Diferencia con SharedWorkers
Usa BroadcastChannel
para casos simples en los que necesitas enviar un mensaje a varias ventanas, pestañas o trabajadores.
Para casos de uso más complejos, como administrar bloqueos, estados compartidos, sincronizar recursos entre un servidor y varios clientes o compartir una conexión de WebSocket con un host remoto, los trabajadores compartidos son la solución más adecuada.
Diferencia con la API de MessageChannel
La principal diferencia entre la API de Channel Messaging y BroadcastChannel
es que esta última es un medio para enviar mensajes a varios objetos de escucha (uno a muchos). MessageChannel
está diseñado para la comunicación directa entre secuencias de comandos. También es más complejo, ya que debes configurar canales con un puerto en cada extremo.
Detección de funciones y compatibilidad con navegadores
Actualmente, Chrome 54, Firefox 38 y Opera 41 admiten la API de Broadcast Channel.
if ('BroadcastChannel' in self) {
// BroadcastChannel API supported!
}
En cuanto a los polyfills, hay algunos:
- https://gist.github.com/alexis89x/041a8e20a9193f3c47fb
- https://gist.github.com/inexorabletash/52f437d1451d12145264
No las probé, por lo que tu kilometraje puede variar.