/ proof-of-concept / phase-perception-2
Sign in
proof-of-concept / phase-perception-2
phase-perception-2 / src / renderer.ts
50 lines · 1.4 KBBlameHistoryRaw
1/**
2 * Main-thread handle on the render worker. Requests are coalesced: while one
3 * render is running, only the most recent further request is kept, and any
4 * request it replaces resolves to null.
5 */
6import type { RenderRequest, RenderResult } from './types'
8type Job = { req: RenderRequest; resolve: (r: RenderResult | null) => void; reject: (e: Error) => void }
10export class Renderer {
11 private worker: Worker
12 private busy: Job | null = null
13 private queued: Job | null = null
14 private nextId = 1
16 constructor() {
17 this.worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })
18 this.worker.onmessage = (e: MessageEvent<RenderResult>) => {
19 const job = this.busy
20 this.busy = null
21 job?.resolve(e.data)
22 this.pump()
23 }
24 this.worker.onerror = (e) => {
25 const job = this.busy
26 this.busy = null
27 job?.reject(new Error(e.message || 'render failed'))
28 this.pump()
29 }
30 }
32 request(req: Omit<RenderRequest, 'id'>): Promise<RenderResult | null> {
33 return new Promise((resolve, reject) => {
34 if (this.queued) this.queued.resolve(null)
35 this.queued = { req: { ...req, id: this.nextId++ }, resolve, reject }
36 this.pump()
37 })
38 }
40 private pump(): void {
41 if (this.busy || !this.queued) return
42 this.busy = this.queued
43 this.queued = null
44 this.worker.postMessage(this.busy.req)
45 }
47 terminate(): void {
48 this.worker.terminate()
49 }
↑↓ move↵openescclose