API BroadcastChannel – Bus de messages pour le Web

Eric Bidelman

L'API BroadcastChannel permet aux scripts de même origine d'envoyer des messages à d'autres contextes de navigation. Il s'agit d'un simple bus de messages permettant d'utiliser la sémantique Pub/Sub entre les fenêtres/onglets, les cadres iFrame, 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 les contextes de navigation. c'est-à-dire la communication entre des fenêtres/onglets, des iFrames, des Web workers et des service workers. Les messages publiés sur une chaîne donnée sont transmis à tous les auditeurs de cette chaîne.

Le constructeur BroadcastChannel utilise un seul paramètre: le nom d'un canal. Ce nom permet d'identifier la chaîne et de le vivre quel que soit le contexte 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 d'un fichier

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

Une chaîne ne diffuse pas d'annonces sur elle-même. Par conséquent, si un écouteur onmessage se trouve sur la même page qu'un postMessage() vers le même canal, cet événement message ne se déclenche pas.

Différences avec d'autres techniques

À ce stade, vous vous demandez peut-être comment cela se rapporte à 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 d'elles a un but. L'API Broadcast Channel est conçue pour faciliter la communication un à plusieurs entre des scripts sur la même origine.

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

  • Détecter les actions des utilisateurs dans les autres onglets
  • Savoir quand un utilisateur se connecte à un compte dans une autre fenêtre ou un autre onglet.
  • Demander à un collaborateur d'effectuer des tâches en arrière-plan
  • Sachez quand un service a terminé l'exécution d'une action.
  • Lorsque l'utilisateur importe une photo dans une fenêtre, faites-la passer aux autres pages ouvertes.

Exemple – 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>

Prenons un autre exemple : vous souhaitez demander à un service worker de supprimer le contenu mis en cache après que l'utilisateur a modifié son "paramètre de stockage hors connexion" dans votre application. Vous pouvez supprimer ses caches à l'aide de window.caches, mais le service worker contient peut-être déjà un utilitaire pour effectuer cette opération. Nous pouvons utiliser l'API Broadcast Channel pour réutiliser ce code ! Sans l'API Broadcast Channel, vous devriez boucler les résultats de self.clients.matchAll() et appeler postMessage() sur chaque client afin d'établir la communication d'un service worker avec tous ses clients (code qui effectue cette opération). L'utilisation d'un canal de diffusion rend ce O(1) au lieu de O(N).

Exemple : Demandez à un service worker de vider le 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 conserver de 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 plusieurs origines. L'API Broadcast Channel a la même origine. Comme les messages ont la même origine, il n'est pas nécessaire de les valider comme auparavant 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);
};

Il vous suffit de vous "abonner" à une chaîne donnée et de bénéficier d'une communication sécurisée et bidirectionnelle !

Différence avec les SharedWorkers

Utilisez BroadcastChannel dans les cas simples où vous devez envoyer un message à plusieurs fenêtres/onglets ou nœuds de calcul, le cas échéant.

Pour les cas d'utilisation plus complexes tels que la gestion de serrures, l'état partagé, 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 constituent 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 API permet de distribuer des messages à plusieurs écouteurs (un à plusieurs). MessageChannel est destiné à la communication bi-univoque 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 prise en charge des navigateurs

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

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

Concernant les polyfills, il en existe quelques-uns:

Je n'ai pas essayé ces solutions, donc le kilométrage peut varier.

Ressources