API BroadcastChannel – Bus de messages pour le Web

L'API BroadcastChannel permet aux scripts d'origine identique d'envoyer des messages à d'autres contextes de navigation. Il peut être considéré comme un simple bus de messages qui permet d'appliquer une sémantique Pub/Sub entre les fenêtres/onglets, les iFrames, les Web workers et les service workers.

Principes de base des API

L'API Broadcast Channel est une API simple qui facilite la communication entre différents contextes de navigation. Autrement dit, il s'agit de la communication entre les fenêtres/onglets, les iFrames, les Web workers et les service workers. Les messages publiés sur un canal donné sont distribués à tous les auditeurs de ce canal.

Le constructeur BroadcastChannel n'accepte qu'un seul paramètre: le nom d'un canal. Le nom permet d'identifier la chaîne et se situe dans différents contextes de navigation.

// 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();

Envoyer des messages

Les messages peuvent être des chaînes ou tout autre élément compatible avec l'algorithme de clonage structuré (chaînes, objets, tableaux, Blobs, ArrayBuffer, Map).

Exemple : envoi d'un objet Blob ou File

channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));

Une chaîne ne diffuse aucune annonce vers elle-même. Ainsi, si vous avez un écouteur onmessage sur la même page qu'un postMessage() sur la même chaîne, cet événement message ne se déclenche pas.

Différences avec d'autres techniques

À ce stade, vous vous demandez peut-être en quoi cela est lié à d'autres techniques de transmission de messages telles que WebSockets, SharedWorkers, l'API MessageChannel et window.postMessage(). L'API Broadcast Channel ne remplace pas ces API. Chacune sert un objectif. L'API Broadcast Channel est conçue pour faciliter la communication un à plusieurs entre des scripts ayant la même origine.

Voici quelques cas d'utilisation des chaînes de diffusion:

  • Détecter les actions des utilisateurs dans d'autres onglets
  • Sachez quand un utilisateur se connecte à un compte dans une autre fenêtre ou un autre onglet.
  • Demander à un nœud de calcul d'effectuer des tâches en arrière-plan
  • Sachez quand un service a terminé d'effectuer une action.
  • Lorsque l'utilisateur importe une photo dans une fenêtre, transmettez-la sur d'autres pages ouvertes.

Exemple de page qui sait quand l'utilisateur se déconnecte, même à partir d'un autre onglet ouvert sur le même 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>

Dans un autre exemple, imaginons que vous vouliez demander à un service worker de supprimer le contenu mis en cache une fois que l'utilisateur a modifié son paramètre de stockage hors connexion ; dans votre application. Vous pouvez supprimer ses caches avec window.caches, mais le service worker peut contiennent déjà un utilitaire pour le faire. Nous pouvons utiliser l'API Broadcast Channel pour réutiliser ce code. Sans l'API Broadcast Channel, il vous faudrait effectuer une boucle sur les résultats de self.clients.matchAll() et appeler postMessage() sur chaque client afin d'établir la communication entre un service worker et l'ensemble de ses clients (code réellement utilisé pour effectuer cette opération). L'utilisation d'un canal de diffusion rend cette O(1) au lieu de O(N).

Exemple : Demandez à un service worker de supprimer un cache en réutilisant ses méthodes utilitaires internes.

Dans 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]);

Dans 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});
    });
    }
};

Différence avec postMessage()

Contrairement à postMessage(), vous n'avez plus besoin de gérer une référence à un iFrame ou à un worker pour communiquer avec lui:

// 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() vous permet également de communiquer entre différentes origines. L'API Broadcast Channel est de même origine. Étant donné que les messages proviennent de la même origine, il n'est pas nécessaire de les valider comme nous le faisions avec 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);
};

"S'abonner" simplement vers un canal particulier et d'avoir une communication sécurisée et bidirectionnelle !

Différence avec SharedWorkers

Utilisez BroadcastChannel pour les cas simples où vous devez envoyer un message à plusieurs fenêtres/onglets ou nœuds de calcul.

Pour les cas d'utilisation plus pointus tels que la gestion de verrous, le partage d'états, la synchronisation de ressources entre un serveur et plusieurs clients ou le partage d'une connexion WebSocket avec un hôte distant, les nœuds de calcul partagés sont la solution la plus appropriée.

Différence avec l'API MessageChannel

La principale différence entre l'API Channel Messaging et BroadcastChannel est que cette dernière permet d'envoyer des messages à plusieurs écouteurs (un à plusieurs). MessageChannel est destiné à une communication de type un à un, directement entre les scripts. Elle est également plus complexe, car elle vous oblige à configurer des canaux avec un port à chaque extrémité.

Détection de fonctionnalités et compatibilité avec les navigateurs

Actuellement, Chrome 54, Firefox 38 et Opera 41 sont compatibles avec l'API Broadcast Channel.

if ('BroadcastChannel' in self) {
    // BroadcastChannel API supported!
}

En ce qui concerne les polyfills, il en existe quelques-uns:

Je n'ai pas essayé. Votre kilométrage peut donc varier.

Ressources