web-workers

GitHub

规范 Lichtblick 中 Web Worker 的使用模式,涵盖 ComlinkWrap 生命周期管理、FinalizationRegistry 防泄漏、AbortSignal/OffscreenCanvas/ArrayBuffer 传输处理、Webpack URL 兼容及 SharedWorker 隔离策略。

.github/skills/web-workers/SKILL.md lichtblick-suite/lichtblick

Trigger Scenarios

需要创建 Web Worker 进行后台计算 涉及 OffscreenCanvas 或大二进制数据跨线程传输 需要确保 Worker 资源正确释放以防内存泄漏 使用 SharedWorker 实现多标签页共享

Install

npx skills add lichtblick-suite/lichtblick --skill web-workers -g -y
More Options

Non-standard path

npx skills add https://github.com/lichtblick-suite/lichtblick/tree/develop/.github/skills/web-workers -g -y

Use without installing

npx skills use lichtblick-suite/lichtblick@web-workers

指定 Agent (Claude Code)

npx skills add lichtblick-suite/lichtblick --skill web-workers -a claude-code -g -y

安装 repo 全部 skill

npx skills add lichtblick-suite/lichtblick --all -g -y

预览 repo 内 skill

npx skills add lichtblick-suite/lichtblick --list

SKILL.md

Frontmatter
{
    "name": "web-workers",
    "description": "Web Worker patterns used throughout the Lichtblick codebase: Comlink integration, ComlinkWrap lifecycle, transfer handlers, OffscreenCanvas, SharedWorker isolation, and testing utilities."
}

Web Workers Skill

Standard Pattern: ComlinkWrap

All Worker communication in Lichtblick uses Comlink with the ComlinkWrap utility for safe lifecycle management.

Worker Creation (main thread)

import { ComlinkWrap } from "@lichtblick/den/worker";

const worker = new Worker(
  new URL("./MyWorker.worker", import.meta.url),  // webpack-compatible URL
);

const { remote, dispose } = ComlinkWrap<MyWorkerAPI>(worker);

// Use the remote API
const result = await remote.process(data);

// Cleanup when done
dispose(); // releases Comlink proxy + terminates worker

Worker Implementation (worker thread)

import * as Comlink from "@lichtblick/comlink";

class MyWorkerImpl {
  async process(data: Uint8Array): Promise<Result> {
    // Heavy computation here
    return result;
  }
}

Comlink.expose(new MyWorkerImpl());

Key file: packages/den/worker/ComlinkWrap.ts

FinalizationRegistry Cleanup

ComlinkWrap returns a dispose function, but the project also uses FinalizationRegistry as a safety net:

const registry = new FinalizationRegistry<() => void>((dispose) => {
  dispose(); // Worker terminated when wrapper is garbage collected
});

// In constructor:
registry.register(this, dispose);

This prevents Worker leaks if the wrapping object is GC'd without explicit disposal.

Transfer Handlers

AbortSignal Transfer

import { abortSignalTransferHandler } from "@lichtblick/comlink-transfer-handlers";

// Register BEFORE any Comlink communication
Comlink.transferHandlers.set("abortsignal", abortSignalTransferHandler);

Allows passing AbortSignal across Worker boundaries — used by WorkerIterableSource to cancel iteration.

OffscreenCanvas Transfer

const offscreenCanvas = canvas.transferControlToOffscreen();

const { remote, dispose } = ComlinkWrap<RendererService>(worker);
await remote.init(
  Comlink.transfer(
    { canvas: offscreenCanvas, devicePixelRatio: window.devicePixelRatio },
    [offscreenCanvas],  // Transfer list
  ),
);

Used by: Plot panel (OffscreenCanvasRenderer), Chart component.

ArrayBuffer Transfer

// Transfer large binary data to Worker (zero-copy)
await remote.processData(Comlink.transfer(buffer, [buffer.buffer]));
// After transfer: buffer.byteLength === 0 (detached)

Worker URL Pattern (Webpack)

All Worker URLs use the import.meta.url pattern for webpack compatibility:

new Worker(new URL("./MyWorker.worker", import.meta.url));
  • File must be named *.worker.ts (webpack recognizes this pattern)
  • babel-plugin-transform-import-meta handles the URL resolution
  • Each Worker file is bundled as a separate chunk

SharedWorker Pattern

Used by UserScriptPlayer for script execution:

new SharedWorker(new URL("./transformerWorker/index", import.meta.url), {
  name: uuidv4(),  // Unique name prevents sharing between tabs
});
  • SharedWorker chosen for memory efficiency (shared code across script instances)
  • Unique name per instance prevents cross-tab Worker sharing (intentional isolation)

Testing Workers

makeComlinkWorkerMock

import { makeComlinkWorkerMock } from "@lichtblick/den/testing";

// Replace global Worker constructor with a mock that uses in-process Comlink
Object.defineProperty(global, "Worker", {
  writable: true,
  value: makeComlinkWorkerMock(() => new ActualImplementation()),
});

Located in packages/den/testing/makeComlinkWorkerMock.ts:

  • Creates an in-process Comlink channel (no actual Worker thread)
  • Allows unit testing Worker-based code without spawning real threads
  • Uses EventEmitter to simulate postMessage / onmessage

Workers in the Codebase

Location Purpose Pattern
IterablePlayer/WorkerIterableSource.ts Data source parsing ComlinkWrap + AbortSignal
Plot/OffscreenCanvasRenderer.ts Chart.js rendering ComlinkWrap + OffscreenCanvas
Plot/builders/TimestampDatasetsBuilder.ts Dataset building ComlinkWrap + FinalizationRegistry
ThreeDeeRender/renderables/Images/WorkerImageDecoder.ts Image decoding ComlinkWrap
UserScriptPlayer/index.ts Script execution SharedWorker + unique name
FoxgloveWebSocketPlayer/WorkerSocketAdapter.ts WebSocket I/O Raw Worker + postMessage
components/Chart/index.tsx Legacy chart rendering WebWorkerManager + Rpc

Performance Considerations

  1. Transfer vs Copy: Always use Comlink.transfer() for large ArrayBuffers
  2. Worker startup: Workers are created lazily — first use incurs startup cost
  3. Proxy cleanup: Always call dispose() or rely on FinalizationRegistry
  4. Message overhead: Small frequent messages have higher overhead than batched large messages
  5. SharedWorker caveats: Debugging is harder (separate DevTools), errors may be silent

Version History

  • cab9317 Current 2026-07-24 12:17

Same Skill Collection

.github/skills/3d-rendering/SKILL.md
.github/skills/caching-internals/SKILL.md
.github/skills/deserialization/SKILL.md
.github/skills/e2e-playwright-mcp/SKILL.md
.github/skills/electron-internals/SKILL.md
.github/skills/extensions-internals/SKILL.md
.github/skills/layouts-internals/SKILL.md
.github/skills/mcap-format/SKILL.md
.github/skills/message-path/SKILL.md
.github/skills/message-pipeline/SKILL.md
.github/skills/panel-extension-api/SKILL.md
.github/skills/panel-image/SKILL.md
.github/skills/panel-log/SKILL.md
.github/skills/panel-map/SKILL.md
.github/skills/panel-raw-messages/SKILL.md
.github/skills/panel-state-transitions/SKILL.md
.github/skills/panel-user-scripts/SKILL.md
.github/skills/performance/SKILL.md
.github/skills/player-internals/SKILL.md
.github/skills/plot-internals/SKILL.md
.github/skills/remote-caching/SKILL.md
.github/skills/test-conventions/SKILL.md
.github/skills/theme/SKILL.md
.github/skills/unit-testing/SKILL.md
.github/skills/websocket-connection/SKILL.md

Metadata

Files
0
Version
cab9317
Hash
1ae8c30a
Indexed
2026-07-24 12:17

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-07 11:26
浙ICP备14020137号-1 $Carte des visiteurs$