Skip to content

Commit

Permalink
chore(worker): eliminate side effects from constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
fallenoak committed Jan 6, 2024
1 parent 296942b commit 10dabe1
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 28 deletions.
24 changes: 0 additions & 24 deletions src/lib/worker/SceneWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,17 @@ import { RESPONSE_STATUS } from './const.js';
import { WorkerRequest, WorkerResponse } from './types.js';

class SceneWorker {
#initialized = false;
#initializing: Promise<void>;
#initializingResolve: (value?: any) => void;
#initializingReject: (reason?: any) => void;

constructor() {
self.addEventListener('message', (event: MessageEvent<WorkerRequest>) => {
const request = event.data;

this.#handleRequest(request).catch((error: Error) => this.#handleError(request, error));
});

this.#initializing = new Promise((resolve, reject) => {
this.#initializingResolve = resolve;
this.#initializingReject = reject;
});
}

initialize(...args: any[]) {}

async #handleRequest(request: WorkerRequest) {
// Hold other requests back until initialize is handled
if (!this.#initialized && request.func !== 'initialize') {
await this.#initializing;
}

const func = this[request.func];
if (!func) {
throw new Error(`Invalid function name: ${request.func}`);
Expand All @@ -47,10 +32,6 @@ class SceneWorker {
}

#handleError(request: WorkerRequest, error: Error) {
if (!this.#initialized && request.func === 'initialize') {
this.#initializingReject(error);
}

const response: WorkerResponse = {
id: request.id,
status: RESPONSE_STATUS.STATUS_ERROR,
Expand All @@ -61,11 +42,6 @@ class SceneWorker {
}

#handleSuccess(request: WorkerRequest, value: any, transfer: Transferable[] = []) {
if (!this.#initialized && request.func === 'initialize') {
this.#initialized = true;
this.#initializingResolve();
}

const response: WorkerResponse = {
id: request.id,
status: RESPONSE_STATUS.STATUS_SUCCESS,
Expand Down
43 changes: 39 additions & 4 deletions src/lib/worker/SceneWorkerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,66 @@ import { WorkerResponse } from './types.js';
import { RESPONSE_STATUS } from './const.js';

class SceneWorkerController {
#initialized = false;
#initializeArgs: any[];
#initializing: Promise<void>;
#initializingResolve: (value?: any) => void;
#initializingReject: (reason?: any) => void;

#worker: Worker;

#nextId = 0;
#pending = new Map<number, { resolve: (value: any) => void; reject: (reason: any) => void }>();

constructor(createWorker: () => Worker, ...initializeArgs: any[]) {
this.#worker = createWorker();
this.#initializeArgs = initializeArgs;

this.#worker.addEventListener('message', (event: MessageEvent) => {
this.#handleResponse(event.data);
});
}

this.request('initialize', ...initializeArgs).catch((error) => console.error(error));
async request(func: string, ...args: any[]): Promise<any> {
if (!this.#initialized) {
if (this.#initializing) {
await this.#initializing;
} else {
await this.#initialize();
}
}

return this.#request(func, args);
}

request(func: string, ...args: any[]): Promise<any> {
#request(func: string, args: any[]): Promise<any> {
const id = this.#nextId++;

const requestPromise = new Promise((resolve, reject) => {
const promise = new Promise((resolve, reject) => {
this.#pending.set(id, { resolve, reject });
});

this.#worker.postMessage({ id, func, args });

return requestPromise;
return promise;
}

#initialize() {
this.#initializing = new Promise((resolve, reject) => {
this.#initializingResolve = resolve;
this.#initializingReject = reject;
});

this.#request('initialize', this.#initializeArgs)
.then((response) => {
this.#initialized = true;
this.#initializingResolve(response);
})
.catch((error) => {
this.#initializingReject(error);
});

return this.#initializing;
}

#handleResponse(response: WorkerResponse) {
Expand Down

0 comments on commit 10dabe1

Please sign in to comment.