@pricingmonkey/tangi
v2.3.0
Published
Lightweight actor library for Web Workers inspired by Akka
Downloads
205
Readme
👧 tangi
ತಂಗಿ [tangi] Kan. younger sister
ಅಕ್ಕ [akka] Kan. older sister
Lightweight actor library for Web Workers inspired by Akka.
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
type PingMessage = {
_tag: "PING";
}
type PongMessage = {
_tag: "PONG";
}
main.ts
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
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
type PingMessage = {
_tag: "PING";
}
type PongMessage = {
_tag: "PONG";
}
main.ts
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
type PingMessage = {
_tag: "PING";
}
type PongMessage = {
_tag: "PONG";
}
main.ts
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
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
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) and:
- types become:
type PingMessage = {
_tag: "PING";
groupId: string;
id: string;
}
type CancelMessage = {
_tag: "CANCEL";
groupId: string;
id: string;
}
- cancellation operator calls change to:
cancellationOperator.register(message.contextId, message.id, task);
cancellationOperator.unregister(message.contextId, message.id);
cancellationOperator.cancel(message.contextId);