Skip to content

Instantly share code, notes, and snippets.

@muke1908
Created January 4, 2023 08:42
Show Gist options
  • Save muke1908/c9e3fbf4b91af0c143d8108f2541396b to your computer and use it in GitHub Desktop.
Save muke1908/c9e3fbf4b91af0c143d8108f2541396b to your computer and use it in GitHub Desktop.
Run timer on webworker for BG ops.
/*
const timer = new WorkerBasedTimer();
const id = timer.setInterval(()=> {
// do something
}, 200);
timer.clearInterval(id);
*/
type TimerData = {
id: number;
callback: (arg: any) => void;
args: any;
};
export class WorkerBasedTimer {
private readonly timerId = 1;
private timerData: TimerData;
private doWork: Worker;
private timeout: number;
private isTimerClearing: boolean;
private deferredTimerOp: {
callback: TimerData['callback'],
timeout: number,
args?: any
}
constructor() {
this.createWorker();
}
public static isSupported(): boolean {
}
public setInterval(callback: TimerData['callback'], timeout: number, args?: any): number {
// deffer the timer creation until the previously created timer is cleared on worker.
if(this.isTimerClearing) {
this.deferredTimerOp = {
callback,
timeout,
args
}
return this.timerId;
}
if (this.timerData) {
return null;
}
this.timerData = {
id: this.timerId,
callback,
args,
};
this.timeout = timeout;
this.doWork.postMessage({ next: true, id: this.timerId, ms: this.timeout });
return this.timerId;
}
public clearInterval(id: number): void {
if (!id) {
return;
}
this.isTimerClearing = true;
this.doWork.postMessage({ stop: true, id });
}
public disposeTimerWorker() {
if (!this.doWork) {
return;
}
this.timerData = null;
this.doWork.removeEventListener('message', this.workerMessageHandler);
this.doWork.removeEventListener('error', this.workerErrorHandler);
this.doWork.terminate();
this.doWork = null;
}
private readonly workerMessageHandler = (event: MessageEvent): any => {
switch (event.data.type) {
case 'ping':
return this.onPing();
case 'setIntervalCompleted':
return this.onSetIntervalCompleted(event.data.id);
case 'clearTimeoutCompleted':
return this.onTimeoutClearCompleted(event.data.id);
default:
return undefined;
}
};
private readonly workerErrorHandler = (error: ErrorEvent): void => {
};
private onSetIntervalCompleted(id: number): void {
}
private onTimeoutClearCompleted(id: number): void {
this.timerData = null;
this.isTimerClearing = false;
if(this.deferredTimerOp) {
const { callback, timeout, args } = this.deferredTimerOp;
this.setInterval(callback, timeout, args);
this.deferredTimerOp = null;
}
}
private onPing(): void {
if (this.timerData?.callback) {
this.timerData.callback(this.timerData.args);
this.doWork.postMessage({ next: true, id: this.timerId, ms: this.timeout });
}
}
private createWorker(): void {
try {
// Code to execute in the worker
const blob = new Blob(
[
`'use strict';
var internalId;
var externalId;
(function () {
onmessage = function (event) {
if (event.data.next) {
clearTimeout(internalId);
externalId = event.data.id;
internalId = setTimeout(function () {
postMessage({ type: 'ping', id: externalId });
}, event.data.ms);
} else if (event.data.stop) {
if (event.data.id === externalId) {
clearTimeout(internalId);
}
postMessage({ type: 'clearTimeoutCompleted', id: externalId });
}
};
})();`,
],
{ type: 'text/javascript' },
);
// Create a worker to process timers
this.doWork = new uaa.window.Worker(uaa.window.URL.createObjectURL(blob));
this.doWork.addEventListener('message', this.workerMessageHandler);
this.doWork.addEventListener('error', this.workerErrorHandler);
} catch (error) {
throw error;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment