Giter Site home page Giter Site logo

swivel's Introduction

swivel

Message passing between ServiceWorker and pages made simple

Inspiration

Understanding the raw API for message passing between ServiceWorker and pages can be kind of confusing. There's MessageChannel, ports, postMessage deeply buried in navigator.serviceWorker.controller.postMessage, addEventListener, multiple ServiceWorkerRegistration instances, and even promises are involved.

ServiceWorker is too awesome to let this problem hinder its adoption, so I made swivel in hopes people will find it easier to share messages across pages and their ServiceWorker. For an introduction of ServiceWorker you should look at this article.

I named it swivel mostly because it starts with sw and it wasn't taken on npm. And, because.

Installation

Install it from npm. You can then include swivel in your pages and your ServiceWorker, and Swivel will figure out what API to export depending on whether it's running within the ServiceWorker or a regular web page.

npm i -S swivel

Usage

On your web pages, you can listen for messages from the ServiceWorker. Remember to wait for ServiceWorker to become active, and always feature test to ensure ServiceWorker is available.

if (!('serviceWorker' in navigator)) {
  return;
}
navigator.serviceWorker
  .register('/service-worker.js')
  .then(navigator.serviceWorker.ready)
  .then(function () {
    swivel.on('data', function handler (context, ...data) {
      // do something with ...data
    });
  });

You can also emit messages to the ServiceWorker from your pages.

swivel.emit('data', ...data);

Emitting returns a Promise, in case you want to wait until the message is transferred to do something else.

swivel.emit('data', ...data).then(function () {
  // ... more swivelling!
});

In your ServiceWorker, the API barely changes. You can listen to messages posted from web pages with swivel.emit using swivel.on in the ServiceWorker code.

swivel.on('data', function handler (context, ...data) {
  // do something with ...data
});

If you need to reply to this particular page in the ServiceWorker, you could just use code like the following.

swivel.on('data', function handler (context, ...data) {
  context.reply('data', ...response);
});

You guessed correctly, context.reply returns a Promise.

swivel.on('data', function handler (context, ...data) {
  context.reply('data', ...response).then(function () {
    // ... more swivelling!
  });
});

You can also emit messages to every page using swivel.broadcast.

swivel.broadcast(type, ...data);

Broadcast also returns a Promise that awaits all client.postMessage signals to be processed.

swivel.broadcast(type, ...data).then(function () {
  // ... more swivelling!
});

Pages can then listen for the type event, which will go out to every page controlled by the ServiceWorker.

swivel.on(type, function (...data) {
  // do something with ...data
});

Importing

The swivel package performs a convenient bit of feature testing in order to decide what API to export. In your web pages, it'll export an API corresponding to web pages. In a ServiceWorker script, it'll export an API corresponding to the ServiceWorker side.

Automatic

import swivel from 'swivel'

If you prefer being explicit, you could import the individual modules separately.

Manual

Here's an example of manual setup for your web pages.

import createChannel from 'swivel/page'
var swivel = createChannel()

Here's an example of manual setup for your ServiceWorker script.

import createChannel from 'swivel/worker'
var swivel = createChannel()

API in Web Pages

The public swivel API exports a number of methods designed for web pages.

swivel.on(type, handler)

This method listens for events emitted by the ServiceWorker API. You can bind an event handler that receives all arguments emitted at that level. It can be triggered from a ServiceWorker in 3 different ways. Returns swivel for chaining.

The following methods -- when called from a ServiceWorker -- trigger handlers registered with swivel.on(type, fn) on web pages.

The handler has a context, ...data signature.

A context.broadcast flag in the handler indicates whether the message was broadcasted or unicasted by the ServiceWorker.

Example
swivel.on('data', function handler (context, ...data) {
  console.log(...data);
});

swivel.once(type, handler)

Equivalent to swivel.on but will only ever be called once. Returns swivel for chaining.

Example
swivel.once('data', function handler (context, ...data) {
  console.log(...data);
});

swivel.off(type, handler)

Unregisters a handler of type type that was previously registered using swivel.on or swivel.once. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  swivel.off('data', handler);
});

swivel.emit(type, ...data)

This method posts a message to the ServiceWorker. You can then use swivel.on(type, handler) from the ServiceWorker to listen to it.

Example
swivel.emit('data', { foo: 'bar' }, 'baz');

This method returns a Promise so you can await for the message to be successfully transferred to the ServiceWorker.

Example
swivel.emit('data', { foo: 'bar' }, 'baz').then(function () {
  console.log('success');
});

swivel.at(worker)

The swivel.at(worker) method returns an API identical to swivel that talks strictly with worker. The default swivel API interacts with the worker found at navigator.serviceWorker.controller. You can use as many channels as necessary.

Example
navigator.serviceWorker
  .getRegistration('/other')
  .then(function (registration) {
    var otherChannel = swivel.at(registration.active);
    otherChannel.emit('data', { hello: 'world' });
    otherChannel.on('data', function handler (context, ...data) {
      console.log(...data);
    });
  });

API in ServiceWorker

The public swivel API exports a number of methods designed for ServiceWorker scripts.

swivel.broadcast(type, ...data)

This method sends a message of type type from the ServiceWorker to every client it controls. Pages can listen for broadcasted messages using swivel.on or swivel.once.

Example
swivel.broadcast('urgent', ...data);

This method returns a Promise so you can await for the message to be successfully transferred to all clients.

Example
swivel.broadcast('urgent', ...data).then(function () {
  console.log('success');
});

swivel.emit(clientId, type, ...data)

During fetch events in a ServiceWorker, it's possible to message a client using swivel.emit. The web page can then receive and handle the message using swivel.on.

Example
self.addEventListener('fetch', function (e) {
  swivel.emit(e.clientId, 'data', { foo: 'bar' });
});

Furthermore, swivel.emit returns a Promise, so you can await for the message to be successfully transferred.

Example
self.addEventListener('fetch', function (e) {
  swivel.emit(e.clientId, 'data', { foo: 'bar' }).then(function () {
    console.log('success');
  });
});

swivel.on(type, handler)

You can use this method to listen from messages sent from a web page to the ServiceWorker using swivel.emit. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  console.log(...data);
});

The event handler is provided with a context object that allows for replies to messages originating from a web page.

context.reply(type, ...data)

Using this method, your ServiceWorker can reply to messages received from an individual web page.

Example
swivel.on('data', function handler (context, [x, y]) {
  context.reply('result', x * y);
});

Furthermore, context.reply returns a Promise, so you can await for the reply to be successfully transferred.

Example
swivel.on('data', function handler (context, [x, y]) {
  context.reply('result', x * y).then(function () {
    console.log('success');
  });
});

swivel.once(type, handler)

Equivalent to swivel.on but will only ever be called once. Returns swivel for chaining. Also able to use context.reply in handler(context, ...data) for bidirectional communication.

Example
swivel.once('data', function handler (context, [x, y]) {
  context.reply('result', x * y).then(function () {
    console.log('success');
  });
});

swivel.off(type, handler)

Unregisters a handler of type type that was previously registered using swivel.on or swivel.once. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  swivel.off('data', handler);
});

License

MIT

swivel's People

Contributors

bevacqua avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.