BroadcastChannel API – ein Nachrichtenbus für das Web

Eric Bidelman

Die BroadcastChannel API ermöglicht es Skripts mit derselben Quelle, Nachrichten an andere Browserkontexte zu senden. Man kann es sich als einen einfachen Nachrichtenbus vorstellen, der eine Pub/Sub-Semantik zwischen Fenstern/Tabs, iFrames, Web Workern und Service Workern ermöglicht.

API-Grundlagen

Das Broadcast Channel API ist ein einfaches API, das die Kommunikation zwischen Suchkontexten erleichtert. Das heißt, die Kommunikation zwischen Fenstern/Tabs, iFrames, Web Workern und Service Workern. Nachrichten, die in einem bestimmten Kanal gepostet werden, werden allen Hörern dieses Kanals zugestellt.

Der BroadcastChannel-Konstruktor verwendet einen einzelnen Parameter: den Namen eines Kanals. Der Name kennzeichnet den Kanal und ist unabhängig vom jeweiligen Suchkontext.

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

Nachrichten senden

Nachrichten können Strings oder beliebige Elemente sein, die vom Algorithmus für strukturierte Klonen unterstützt werden (Strings, Objekte, Arrays, Blobs, ArrayBuffer, Map).

Beispiel: Blob oder Datei senden

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

Ein Kanal wird nicht an sich selbst übertragen. Wenn sich also ein onmessage-Listener auf derselben Seite wie ein postMessage()-Listener für denselben Kanal befindet, wird dieses message-Ereignis nicht ausgelöst.

Unterschiede zu anderen Verfahren

Sie fragen sich vielleicht, wie dies im Zusammenhang mit anderen Techniken zur Nachrichtenweitergabe wie WebSockets, SharedWorkers, der MessageChannel API und window.postMessage() steht. Die Broadcast Channel API ersetzt diese APIs nicht. Beide erfüllen einen Zweck. Die Broadcast Channel API dient der einfachen 1:n-Kommunikation zwischen Skripts mit demselben Ursprung.

Einige Anwendungsfälle für Übertragungskanäle:

  • Nutzeraktionen auf anderen Tabs erkennen
  • Sie werden informiert, wenn sich ein Nutzer in einem anderen Fenster oder Tab bei einem Konto anmeldet.
  • Einen Worker anweisen, einige Hintergrundarbeiten durchzuführen
  • Sie werden informiert, wenn ein Dienst mit der Ausführung einer Aktion fertig ist.
  • Wenn der Nutzer ein Foto in einem Fenster hochlädt, geben Sie es an andere geöffnete Seiten weiter.

Beispiel – Seite mit Informationen dazu, wann sich der Nutzer abmeldet, auch wenn dies auf einem anderen geöffneten Tab auf derselben Website möglich ist:

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

Angenommen, Sie möchten einen Service Worker anweisen, im Cache gespeicherte Inhalte zu entfernen, nachdem der Nutzer seine "Offlinespeichereinstellung" in Ihrer Anwendung geändert hat. Sie können die Caches des Nutzers mit window.caches löschen. Der Service Worker enthält jedoch möglicherweise bereits ein Dienstprogramm dafür. Mit der Broadcast Channel API können wir diesen Code wiederverwenden. Ohne die Broadcast Channel API müssten Sie die Ergebnisse von self.clients.matchAll() in einer Schleife wiedergeben und postMessage() auf jedem Client aufrufen, um die Kommunikation von einem Service Worker mit allen seinen Clients zu erreichen (tatsächlicher Code). Bei Verwendung eines Broadcast-Kanals ist dieses O(1) anstelle von O(N).

Beispiel – weisen Sie einen Service Worker an, einen Cache zu entfernen und dabei dessen interne Dienstprogrammmethoden wiederzuverwenden.

In „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]);

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

Differenz zu postMessage()

Im Gegensatz zu postMessage() müssen Sie keinen Verweis mehr auf einen iFrame oder Worker verwalten, um mit ihm zu kommunizieren:

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

Mit window.postMessage() können Sie auch ursprungsübergreifend kommunizieren. Die Broadcast Channel API hat denselben Ursprung. Da Nachrichten immer aus demselben Ursprung stammen, ist es nicht erforderlich, sie wie bisher mit window.postMessage() zu validieren:

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

Abonnieren Sie einfach einen bestimmten Kanal und sorgen Sie für eine sichere, bidirektionale Kommunikation.

Unterschied zu SharedWorkers

Verwenden Sie BroadcastChannel für einfache Fälle, in denen Sie eine Nachricht an potenziell mehrere Fenster/Tabs oder Worker senden müssen.

Für komplexere Anwendungsfälle wie die Verwaltung von Sperren, den gemeinsamen Status, das Synchronisieren von Ressourcen zwischen einem Server und mehreren Clients oder die gemeinsame Nutzung einer WebSocket-Verbindung mit einem Remote-Host sind freigegebene Worker die beste Lösung.

Unterschied zur MessageChannel API

Der Hauptunterschied zwischen der Channel Messaging API und der BroadcastChannel besteht darin, dass mit der API Nachrichten an mehrere Listener (1:n) gesendet werden können. MessageChannel dient der direkten Kommunikation zwischen Skripts. Dieser Vorgang ist auch aufwendiger und erfordert die Einrichtung von Kanälen mit einem Port an beiden Enden.

Funktionserkennung und Browserunterstützung

Derzeit unterstützen Chrome 54, Firefox 38 und Opera 41 das Broadcast Channel API.

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

Was Polyfills betrifft, gibt es einige:

Ich habe sie noch nicht ausprobiert. Die Laufzeit kann also variieren.

Ressourcen