terikon / promise-worker-transferable

Promise-based messaging for Web Workers and Service Workers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

promise-worker-transferable Build Status

Modified version of promise-worker library that supports object transferring. Possibly works slower than original for not transferable messages.

As mentioned here, promise-worker library will not get support for blobs and transferables. So here promise-worker-transferable goes.

Goals:

  • Tiny footprint (~2.5kB min+gz)
  • Assumes you have a separate worker.js file (easier to debug, better browser support)
  • Removed from promise-worker and no longer true: JSON.stringifys messages for performance
  • Instead, it's now possbile to transfer blobs, as well as attach transferList array to transfer objects, which works much faster for larger objects.

Live examples:

Usage

Install:

npm install promise-worker-transferable

Inside your main bundle:

// main.js
var PromiseWorker = require('promise-worker-transferable');
var worker = new Worker('worker.js');
var promiseWorker = new PromiseWorker(worker);

promiseWorker.postMessage('ping').then(function (response) {
  // handle response
}).catch(function (error) {
  // handle error
});

// With transferList
promiseWorker.postMessage(pingImageData, [pingImageData.data.buffer]) // pongImageData transferred from main to worker
.then(function (response) {
  // handle response
}).catch(function (error) {
  // handle error
});

Inside your worker.js bundle:

// worker.js
var registerPromiseWorker = require('promise-worker-transferable/register');

registerPromiseWorker(function (message) {
  return 'pong';
});

// With transferList
registerPromiseWorker(function (message, withTransferList) {
  return withTransferList(pongImageData, [pongImageData.data.buffer]); // pongImageData transferred from worker to main 
});

Note that you require() two separate APIs, so the library is split between the worker.js and main file. This keeps the total bundle size smaller.

Message format

The message you send can be any object, array, string, number, etc.:

// main.js
promiseWorker.postMessage({
  hello: 'world',
  answer: 42,
  "this is fun": true
}).then(/* ... */);
// worker.js
registerPromiseWorker(function (message) {
  console.log(message); // { hello: 'world', answer: 42, 'this is fun': true }
});

Promises

Inside of the worker, the registered handler can return either a Promise or a normal value:

// worker.js
registerPromiseWorker(function () {
  return Promise.resolve().then(function () {
    return 'much async, very promise';
  });
});
// main.js
promiseWorker.postMessage(null).then(function (message) {
  console.log(message): // 'much async, very promise'
});

Promise can return withTransferList as well:

// worker.js
registerPromiseWorker(function (_, withTransferList) {
  return Promise.resolve().then(function () {
    return withTransferList(pongImageData, [pongImageData.data.buffer]); // pongImageData transferred to webworker
  });
});
// main.js
promiseWorker.postMessage(null).then(function (message) {
  // message contains pongImageData
});

Error handling

Any thrown errors or asynchronous rejections from the worker will be propagated to the main thread as a rejected Promise. For instance:

// worker.js
registerPromiseWorker(function (message) {
  throw new Error('naughty!');
});
// main.js
promiseWorker.postMessage('whoops').catch(function (err) {
  console.log(err.message); // 'naughty!'
});

Note that stacktraces cannot be sent from the worker to the main thread, so you will have to debug those errors yourself. This library does however, print messages to console.error(), so you should see them there.

Multi-type messages

If you need to send messages of multiple types to the worker, just add some type information to the message you send:

// main.js
promiseWorker.postMessage({
  type: 'en'
}).then(/* ... */);

promiseWorker.postMessage({
  type: 'fr'
}).then(/* ... */);
// worker.js
registerPromiseWorker(function (message) {
  if (message.type === 'en') {
    return 'Hello!';
  } else if (message.type === 'fr') {
    return 'Bonjour!';
  }
});

Service Workers

Communicating with a Service Worker is the same as with a Web Worker. However, you have to wait for the Service Worker to install and start controlling the page. Here's an example:

navigator.serviceWorker.register('sw.js', {
  scope: './'
}).then(function () {
  if (navigator.serviceWorker.controller) {
    // already active and controlling this page
    return navigator.serviceWorker;
  }
  // wait for a new service worker to control this page
  return new Promise(function (resolve) {
    function onControllerChange() {
      navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
      resolve(navigator.serviceWorker);
    }
    navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
  });
}).then(function (worker) { // the worker is ready
  var promiseWorker = new PromiseWorker(worker);
  return promiseWorker.postMessage('hello worker!');
}).catch(console.log.bind(console));

Then inside your Service Worker:

var registerPromiseWorker = require('../register');

registerPromiseWorker(function (msg) {
  return 'hello main thread!';
});

self.addEventListener('activate', function(event) {
  event.waitUntil(self.clients.claim()); // activate right now
});

Browser support

  • Chrome
  • Firefox
  • Safari 8+
  • IE 10+
  • Edge
  • iOS 8+
  • Android 4.4+

If a browser doesn't support Web Workers but you still want to use this library, then you can use pseudo-worker.

For Service Worker support, Chrome 40 and 41 are known to be buggy (see #9), but 42+ are supported.

This library is not designed to run in Node.js.

API

Main bundle

new PromiseWorker(worker)

Create a new PromiseWorker, using the given worker.

PromiseWorker.postMessage(message, optionalTransferList)

Send a message to the worker and return a Promise.

  • message - object - required
    • The message to send.
  • optionalTransferList - array of objects to transfer, just as in usual Worker.postMessage.
  • returns a Promise

Worker bundle

Register a message handler inside of the worker. Your handler consumes a message and returns a Promise or value.

registerPromiseWorker(function)

  • function
    • Takes a message and withTransferList function, returns a Promise or a value. Value can be wrapped with withTransferList. withTransferList gets value and transferList.

Testing the library

First:

npm install

Then to test in Node (using an XHR/PseudoWorker shim):

npm test

Or to test manually in your browser of choice:

npm run test-local

Or to test in a browser using SauceLabs:

npm run test-browser

Or to test in PhantomJS:

npm run test-phantom

Or to test with coverage reports:

npm run coverage

About

Promise-based messaging for Web Workers and Service Workers

License:Apache License 2.0


Languages

Language:JavaScript 100.0%