@vkammerer/postmessage-raf

0.0.8 • Public • Published

A small layer on top of postMessage and requestAnimationFrame to improve performance with web workers.

Installation

npm i @vkammerer/postmessage-raf

Usage

Default mode

In Default mode, the library is nothing more than syntaxic sugar on top of the native postMessage API.

In the main thread:

import { mainMessager } from "@vkammerer/postmessage-raf";

const slaveWorker = new Worker("./slave.js");
const messager = mainMessager({ worker: slaveWorker });

const action = { foo: 'bar' };
messager.post(action);

In "slave.js", the worker:

import { workerMessager } from "@vkammerer/postmessage-raf";

const messager = workerMessager({
  onAction: action => console.log(action.foo); // 'bar'
});

Ping mode  

The point of this library is to optimize the time at which messages are sent between the main and the worker threads, so that every message is exchanged at the beginning of a requestAnimationFrame call.
In order to do that, the worker can call the method startPing on its messager.

In the worker:

messager.startPing();

This triggers the "Ping mode":

  • the main thread will send a "ping" message to the worker on every requestAnimationFrame and the worker thread will respond with a "pong" message.
  • any action posted with messager.post() will not be sent immediately to the other thread, but added to a queue and sent only along with "ping" and "pong" messages.

API

- mainMessager

The function mainMessager takes an single object as parameter, with the following structure:  

const messager = mainMessager({
  worker: new Worker('./slaveWorker.js'),
  // worker instance
  onAction: action => { console.log(`Just received an action of type ${action.type}`) },
  // function to execute on all actions received from the worker
  beforePing: pingCount => {
    console.log(
      `The main thread is about to send a ping.
      The number of pings since startPing was called is ${pingCount}.`;
    )
  },
  afterPing: pingCount => {
    console.log(
      `The main thread just sent a ping.
      The number of pings since startPing was called is ${pingCount}.`;
    )
  }
});

It returns an object with the post method:

const action = {
  foo: 'bar',
  data: ['a', 'b']
}
messager.post(action);
// Sends action to the worker

- workerMessager

The function workerMessager takes an single object as parameter, with the following structure:  

const messager = workerMessager({
  onAction: action => { console.log(`Just received an action of type ${action.type}`) },
  // function to execute on all actions received from the main thread
  beforePong: pongCount => {
    console.log(
      `The worker just received a ping and is about to send a pong.
      The number of pongs since startPing was called is ${pongCount}.`;
    )
  },
  afterPong: pongCount => {
    console.log(
      `The worker just sent a pong.
      The number of pongs since startPing was called is ${pongCount}.`;
    )
  }
  // function executed after the pong message in "Ping mode"
});

It returns an object with the following methods:

post, // Sends action to the worker - see usage here under
startPing, // Initiates "Ping mode"
stopPing, // Terminates "Ping mode" and resumes "Default mode"

Usage:

const action = {
  foo: 'bar',
  data: ['a', 'b']
}
messager.post(action, {
  delay: {
    count: 10,
    // Registers the action to be called at the 10th ping since startPing was called.
    // If the ping has already occured or if the pinging mode is stopped before,
    // the action will be ignored.
    // Not to be used in conjunction with 'index' here under
    index: 12
    // Registers the action to be called 12 pings after the main thread will receive it.
    // If the pinging mode is stopped before, the action will be ignored.
    // Not to be used in conjunction with 'count' here above
  }
});
// Sends action to the main thread

Example diagram

Here under is an example of how a user input U would propagate to the worker application, which could then emit a response R back to the main thread.

mainApp:  -----U-------------------R-----------
pingMsg:  -P-------P-------P-------P-------P---
                   U

pongMsg:  ---P-------P-------P-------P-------P-
                             R
workerApp:-----------U-R-----------------------

As you can see, there is a minimum of two times the duration of requestAnimationFrame calls for the main thread to get a response. If the frame rate is at about 60 FPS, that means we get a response in about 50 ms. This time is satisfying as the user will not notice the latency.

For more information on acceptable latency after user input, see the Response category of the RAIL model.

Package Sidebar

Install

npm i @vkammerer/postmessage-raf

Weekly Downloads

9

Version

0.0.8

License

MIT

Last publish

Collaborators

  • vkammerer