UNPKG

@pricingmonkey/tangi

Version:

Lightweight actor library for Web Workers inspired by Akka

218 lines (172 loc) 4.81 kB
# 👧 tangi ತಂಗಿ [tangi] [Kan.](https://en.wikipedia.org/wiki/Kannada) younger sister ಅಕ್ಕ [akka] [Kan.](https://en.wikipedia.org/wiki/Kannada) older sister Lightweight actor library for Web Workers inspired by [Akka](https://doc.akka.io). ## What is this? Type-safe, production-ready and lightweight messaging layer for Web Workers. Best served with: - https://github.com/webpack-contrib/worker-loader ## Why? For people to scale Web Workers beyond the simple patterns of communication. ## Basic usage **messages.ts** ```typescript type PingMessage = { _tag: "PING"; } type PongMessage = { _tag: "PONG"; } ``` **main.ts** ```typescript import { makeActorContext } from "tangi"; import { PingMessage, PongMessage } from "./messages"; const worker = new (require("worker-loader!./worker"))(); const workerRemoteContext = makeActorContext<PingMessage, never>(worker); const response = await workerRemoteContext.ask<string, PongMessage>(id => ({ _tag: "PING", id })); switch (response._tag) { case "Right": { console.log(response.right); } case "Left": { console.error(response.left); } } ``` **worker.ts** ```typescript import { makeActorContext, REPLY } from "tangi"; import { PongMessage } from "./messages"; const workerLocalContext = makeActorContext<never, PongMessage>(globalThis as any); workerLocalContext.receiveMessage(message => { switch (message._tag) { case "PING": { message[REPLY]({ _tag: "PONG" }); return; } } }); ``` ## Interaction patterns #### Fire and forget Use `workerRemoteContext.tell({ _tag: "FIRE" })`. See example below: **messages.ts** ```typescript type PingMessage = { _tag: "PING"; } type PongMessage = { _tag: "PONG"; } ``` **main.ts** ```typescript import { makeActorContext } from "tangi"; type FireMessage = { _tag: "FIRE"; } const worker = new (require("worker-loader!./worker"))(); const workerRemoteContext = makeActorContext<FireMessage, never>(worker); workerRemoteContext.tell({ _tag: "FIRE" }); ``` #### Request-response Use `workerRemoteContext.ask({ _tag: "PING" })` combined with `workerLocalContext.receiveMessage({ _tag: "PING" })`. See example below: **messages.ts** ```typescript type PingMessage = { _tag: "PING"; } type PongMessage = { _tag: "PONG"; } ``` **main.ts** ```typescript import { makeActorContext } from "tangi"; import { PingMessage, PongMessage } from "./messages"; const worker = new (require("worker-loader!./worker"))(); const workerRemoteContext = makeActorContext<PingMessage, never>(worker); const response = await workerRemoteContext.ask<string, PongMessage>(id => ({ _tag: "PING", id })); console.log(response) ``` **worker.ts** ```typescript import { makeActorContext, REPLY } from "tangi"; import { PongMessage } from "./messages"; const workerLocalContext = makeActorContext<never, PongMessage>(globalThis as any); workerLocalContext.receiveMessage(message => { switch (message._tag) { case "PING": { message[REPLY]({ _tag: "PONG" }); return; } } }); ``` #### Cancellation (single message) **worker.ts** ```typescript import { makeActorContext, REPLY, makeCancellationOperator } from "tangi"; type PingMessage = { _tag: "PING"; id: string; } type CancelMessage = { _tag: "CANCEL"; id: string; } const makeTask = (killSwitch) => { return async () => { for (let i = 0; i < 100000; i++) { await fetch("http://example.org"); if (killSwitch.isCancelled) { return; } } } }; const workerLocalContext = makeActorContext<never, PongMessage>(globalThis as any); const cancellationOperator = makeCancellationOperator(); workerLocalContext.receiveMessage(async message => { switch (message._tag) { case "PING": { const cancellableTask = cancellationOperator.register(message.id, message.id, makeTask); try { await task.promise(); } finally { cancellationOperator.unregister(message.id, message.id); } return; } case "CANCEL": { cancellationOperator.cancel(message.id); return; } } }); ``` #### Cancellation (message groups) Use it to cancel multiple messages in a given group/context. Similar to [Cancellation (single message)](#cancellation-single-message) and: - types become: ```typescript type PingMessage = { _tag: "PING"; groupId: string; id: string; } type CancelMessage = { _tag: "CANCEL"; groupId: string; id: string; } ``` - cancellation operator calls change to: ```typescript cancellationOperator.register(message.contextId, message.id, task); cancellationOperator.unregister(message.contextId, message.id); cancellationOperator.cancel(message.contextId); ``` ## License [Blue Oak Model License](https://blueoakcouncil.org/license/1.0.0)