All Projects β†’ alesgenova β†’ Post Me

alesgenova / Post Me

Licence: mit
πŸ“© Use web Workers and other Windows through a simple Promise API

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects

Projects that are alternatives of or similar to Post Me

Hamsters.js
100% Vanilla Javascript Multithreading & Parallel Execution Library
Stars: ✭ 517 (+29.9%)
Mutual labels:  concurrency, parallel-computing, web-worker, worker
java-multithread
CΓ³digos feitos para o curso de Multithreading com Java, no canal RinaldoDev do YouTube.
Stars: ✭ 24 (-93.97%)
Mutual labels:  concurrency, parallel-computing
iframe-communication
Basic two way iframe communication
Stars: ✭ 88 (-77.89%)
Mutual labels:  communication, iframe
ProtoPromise
Robust and efficient library for management of asynchronous operations in C#/.Net.
Stars: ✭ 20 (-94.97%)
Mutual labels:  promise, concurrency
YACLib
Yet Another Concurrency Library
Stars: ✭ 193 (-51.51%)
Mutual labels:  promise, concurrency
async
Synchronization and asynchronous computation package for Go
Stars: ✭ 104 (-73.87%)
Mutual labels:  promise, concurrency
conquerant
lightweight async/await for Clojure
Stars: ✭ 31 (-92.21%)
Mutual labels:  promise, concurrency
super-workers
🐴 Distribute load on front-end via parallelism
Stars: ✭ 93 (-76.63%)
Mutual labels:  parallel-computing, web-worker
wise-river
Object streaming the way it should be.
Stars: ✭ 33 (-91.71%)
Mutual labels:  promise, concurrency
ibridge
Typesafe iframe bridge for easy parent child bidirectional communication
Stars: ✭ 25 (-93.72%)
Mutual labels:  communication, iframe
event-worker
A simpler way of dealing with Web Workers
Stars: ✭ 18 (-95.48%)
Mutual labels:  worker, promise
Alloy Worker
ι’ε‘δΊ‹εŠ‘ηš„ι«˜ε―η”¨ Web Worker ι€šδΏ‘ζ‘†ζžΆ
Stars: ✭ 349 (-12.31%)
Mutual labels:  web-worker, worker
trainmanjs
TrainmanJS - Cross-Origin Communication Library
Stars: ✭ 16 (-95.98%)
Mutual labels:  communication, iframe
wtsqs
Simplified Node AWS SQS Worker Wrapper
Stars: ✭ 18 (-95.48%)
Mutual labels:  worker, promise
p-ratelimit
Promise-based utility to make sure you don’t call rate-limited APIs too quickly.
Stars: ✭ 49 (-87.69%)
Mutual labels:  promise, concurrency
iworker
Promise-based wrapper for worker_threads
Stars: ✭ 18 (-95.48%)
Mutual labels:  worker, promise
wolfpacs
WolfPACS is an DICOM load balancer written in Erlang.
Stars: ✭ 1 (-99.75%)
Mutual labels:  communication, worker
Penpal
A promise-based library for securely communicating with iframes via postMessage.
Stars: ✭ 197 (-50.5%)
Mutual labels:  promise, iframe
post-messenger
πŸ‘Ά ~1 Kb wrapper of window.postMessage for cross-document communication.
Stars: ✭ 28 (-92.96%)
Mutual labels:  communication, iframe
easy-promise-queue
An easy JavaScript Promise queue which is automatically executed, concurrency controlled and suspendable.
Stars: ✭ 31 (-92.21%)
Mutual labels:  promise, concurrency

workflow status npm package codecov

post-me

Communicate with web Workers and other Windows using a simple Promise based API

diagram

With post-me it is easy for a parent (for example the main app) and a child (for example a worker or an iframe) to expose methods and custom events to each other.

Features

  • πŸ” Parent and child can both expose methods and/or events.
  • πŸ”Ž Strong typing of method names, arguments, return values, as well as event names and payloads.
  • πŸ€™ Seamlessly pass callbacks to the other context to get progress or partial results.
  • πŸ“¨ Transfer arguments/return values/payloads when needed instead of cloning.
  • πŸ”— Establish multiple concurrent connections.
  • 🌱 No dependencies: 2kb gzip bundle.
  • πŸ§ͺ Excellent test coverage.
  • πŸ‘ Open source (MIT)

Demo

In this live demo the main window communicates with a web worker and an iframe (source).

Content:

  1. Install
  2. Basic Usage
  3. Typescript Support
  4. Other Uses
  5. Callbacks as parameters
  6. Transfer vs Clone
  7. Debugging
  8. Parallel Programming
  9. API Documentation
  10. References

Install

Import post-me as a module:

npm install post-me
import { ParentHandshake } from 'post-me';

Import post-me as a script:

<script src="https://unpkg.com/post-me/dist/index.js"></script>

<script>
  const ParentHandshake = PostMe.ParentHandshake;
</script>

Usage

In the example below, the parent application calls methods exposed by the worker and listens to events emitted by it.

For the sake of simiplicity, only the worker is exposing methods and events, however the parent could do it as well.

Parent code:

import { ParentHandshake, WorkerMessenger } from 'post-me';

const worker = new Worker('./worker.js');

const messenger = new WorkerMessenger({ worker });

ParentHandshake(messenger).then((connection) => {
  const remoteHandle = connection.remoteHandle();

  // Call methods on the worker and get the result as a promise
  remoteHandle.call('sum', 3, 4).then((result) => {
    console.log(result); // 7
  });

  // Listen for a specific custom event from the worker
  remoteHandle.addEventListener('ping', (payload) => {
    console.log(payload) // 'Oh, hi!'
  });
});

Worker code:

import { ChildHandshake, WorkerMessenger } from 'post-me';

// Methods exposed by the worker: each function can either return a value or a Promise.
const methods = {
  sum: (x, y) => x + y,
  mul: (x, y) => x * y
}

const messenger = WorkerMessenger({worker: self});
ChildHandshake(messenger, methods).then((connection) => {
  const localHandle = connection.localHandle();

  // Emit custom events to the app
  localHandle.emit('ping',  'Oh, hi!');
});

Typescript

Using typescript you can ensure that the parent and the child are using each other's methods and events correctly. Most coding mistakes will be caught during development by the typescript compiler.

Thanks to post-me extensive typescript support, the correctness of the following items can be statically checked during development:

  • Method names
  • Argument number and types
  • Return values type
  • Event names
  • Event payload type

Below a modified version of the previous example using typescript.

Types code:

// types.ts

export type WorkerMethods = {
  sum: (x: number, y: number) => number;
  mul: (x: number, y: number) => number;
}

export type WorkerEvents = {
  'ping': string;
}

Parent Code:

import {
 ParentHandshake, WorkerMessenger, RemoteHandle
} from 'post-me';

import { WorkerMethods, WorkerEvents } from './types';

const worker = new Worker('./worker.js');

const messenger = new WorkerMessenger({ worker });

ParentHandshake(messenger).then((connection) => {
  const remoteHandle: RemoteHandle<WorkerMethods, WorkerEvents>
    = connection.remoteHandle();

  // Call methods on the worker and get the result as a Promise
  remoteHandle.call('sum', 3, 4).then((result) => {
    console.log(result); // 7
  });

  // Listen for a specific custom event from the app
  remoteHandle.addEventListener('ping', (payload) => {
    console.log(payload) // 'Oh, hi!'
  });

  // The following lines have various mistakes that will be caught by the compiler
  remoteHandle.call('mul', 3, 'four'); // Wrong argument type
  remoteHandle.call('foo'); // 'foo' doesn't exist on WorkerMethods type
});

Worker code:

import { ChildHandshake, WorkerMessenger, LocalHandle } from 'post-me';

import { WorkerMethods, WorkerEvents } from './types';

const methods: WorkerMethods = {
  sum: (x: number, y: number) => x + y,
  mul: (x: number, y: number) => x * y,
}

const messenger = WorkerMessenger({worker: self});
ChildHandshake(messenger, methods).then((connection) => {
  const localHandle: LocalHandle<WorkerMethods, WorkerEvents>
    = connection.localHandle();

  // Emit custom events to the worker
  localHandle.emit('ping',  'Oh, hi!');
});

Other Uses

post-me can establish the same level of bidirectional communications not only with workers but with other windows too (e.g. iframes) and message channels.

Internally, the low level differences between communicating with a Worker, a Window, or a MessageChannel have been abstracted, and the Handshake will accept any object that implements the Messenger interface defined by post-me.

This approach makes it easy for post-me to be extended by its users.

A Messenger implementation for communicating between Windows and MessagePorts is already provided in the library (WindowMessenger and PortMessenger).

Windows

Here is an example of using post-me to communicate with an iframe.

Parent code:

import { ParentHandshake, WindowMessenger } from 'post-me';

// Create the child window any way you like (iframe here, but could be popup or tab too)
const childFrame = document.createElement('iframe');
const childWindow = childFrame.contentWindow;

// For safety it is strongly adviced to pass the explicit child origin instead of '*'
const messenger = new WindowMessenger({
  localWindow: window,
  remoteWindow: childWindow,
  remoteOrigin: '*'
});

ParentHandshake(messenger).then((connection) => {/* ... */});

Child code:

import { ChildHandshake, WindowMessenger } from 'post-me';

// For safety it is strongly adviced to pass the explicit child origin instead of '*'
const messenger = new WindowMessenger({
  localWindow: window,
  remoteWindow: window.parent,
  remoteOrigin: '*'
});

ChildHandshake(messenger).then((connection) => {/* ... */});

MessageChannels

Here is an example of using post-me to communicate over a MessageChannel.

import { ParentHandshake, ChildHandshake, PortMessenger } from 'post-me';

// Create a MessageChannel
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;

// In the real world  port1 and port2 would be transferred to other workers/windows
{
  const messenger = new PortMessenger({port: port1});
  ParentHandshake(messenger).then(connection => {/* ... */});
}
{
  const messenger = new PortMessenger({port: port2});
  ChildHandshake(messenger).then(connection => {/* ... */});
}

Callbacks as call parameters

Even though functions cannot actually be shared across contexts, with a little magic under the hood post-me let's you pass callback functions as arguments when calling a method on the other worker/window.

Passing callbacks can be useful to obtain progress or partial results from a long running task.

Parent code:

//...
ParentHandshake(messenger).then(connection => {
  const remoteHandle = connection.remoteHandle();

  const onProgress = (progress) => {
    console.log(progress); // 0.25, 0.5, 0.75
  }

  remoteHandle.call("slowSum", 2, 3, onProgress).then(result => {
    console.log(result); // 5
  });
});

Worker code:

const methods = {
  slowSum: (x, y, onProgress) => {
    onProgress(0.25);
    onProgress(0.5);
    onProgress(0.75);

    return x + y;
}
// ...
ChildHandshake(messenger, methods).then(connection => {/* */})

Transfer vs Clone

By default any call parameter, return value, and event payload is cloned when passed to the other context.

While in most cases this doesn't have a significant impact on performance, sometimes you might need to transfer an object instead of cloning it. NOTE: only Transferable objects can be transfered (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas).

post-me provides a way to optionally transfer objects that are part of a method call, return value, or event payload.

In the example below, the parent passes a very large array to a worker, the worker modifies the array in place, and returns it to the parent. Transfering the array instead of cloning it twice can save significant amounts of time.

Parent code:

// ...

ParentHandshake(messenger).then((connection) => {
  const remoteHandle = connection.remoteHandle();

  // Transfer the buffer of the array parameter of every call that will be made to 'fillArray'
  remoteHandle.setCallTransfer('fillArray', (array, value) => [array.buffer]);
  {
    const array = new Float64Array(100000000);
    remoteHandle.call('fillArray', array, 5);
  }

  // Transfer the buffer of the array parameter only for this one call made to 'scaleArray'
  {
    const array = new Float64Array(100000000);
    const args = [array, 2];
    const callOptions = { transfer: [array.buffer] };
    remoteHandle.customCall('scaleArray', args, callOptions);
  }
});

Worker code:

// ...

const methods = {
  fillArray: (array, value) => {
    array.forEach((_, i) => {array[i] = value});
    return array;
  },
  scaleArray: (buffer, type value) => {
    array.forEach((a, i) => {array[i] = a * value});
    return array;
  }
}

ChildHandshake(messenger, model).then((connection) => {
  const localHandle = connection.localHandle();

  // For each method, declare which parts of the return value should be transferred instead of cloned.
  localHandle.setReturnTransfer('fillArray', (result) => [result.buffer]);
  localHandle.setReturnTransfer('scaleArray', (result) => [result.buffer]);
});

Debugging

You can optionally output the internal low-level messages exchanged between the two ends.

To enable debugging, simply decorate any Messenger instance with the provided DebugMessenger decorator.

You can optionally pass to the decorator your own logging function (a glorified console.log by default), which can be useful to make the output more readable, or to inspect messages in automated tests.

import { ParentHandshake, WindowMessenger, DebugMessenger } from 'post-me';

import debug from 'debug';          // Use the full feature logger from the debug library
// import { debug } from 'post-me'; // Or the lightweight implementation provided

let messenger = new WindowMessenger({
  localWindow: window,
  remoteWindow: childWindow,
  remoteOrigin: '*'
});

// To enable debugging of each message exchange, decorate the messenger with DebugMessenger
const log = debug('post-me:parent'); // optional
messenger = DebugMessenger(messenger, log);

ParentHandshake(messenger).then((connection) => {
  // ...
});

Output: debug output

Parallel Programming

@post-me/mpi is an experimental library to write parallel algorithms that run on a pool of workers using a MPI-like syntax. See the dedicated README for more information.

API Documentation

The full API reference can be found here.

References

The post-me API is loosely inspired by postmate, with several major improvements and fixes to outstanding issues:

  • Native typescript support
  • Method calls can have both arguments and a return value: (#94)
  • Parent and child can both expose methods and/or events (instead of child only): #118
  • Exceptions that occur in a method call can be caught by the caller.
  • Better control over handshake origin and attempts: (#150, #195)
  • Multiple listeners for each event: (#58)
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].