Skip to content

Instantly share code, notes, and snippets.

@TimvanScherpenzeel
Last active October 14, 2020 12:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TimvanScherpenzeel/d0035cf04e8224da759611fa36c93a64 to your computer and use it in GitHub Desktop.
Save TimvanScherpenzeel/d0035cf04e8224da759611fa36c93a64 to your computer and use it in GitHub Desktop.
A re-usable thread implementation
// Implementation based on https://github.com/developit/greenlet and https://github.com/developit/task-worklet
class Thread {
private taskId = 0;
private promises: any = {};
private worker: Worker | null = new Worker(
URL.createObjectURL(
new Blob(
[
`(${() => {
self.onmessage = (e: MessageEvent) => {
Promise.resolve(Function(`return(${e.data[1]})(${e.data[2]})`)())
.then((r) => {
(self as any).postMessage(
['r', r, e.data[0], 0],
[r].filter(
(x: unknown) =>
x instanceof ArrayBuffer ||
x instanceof MessagePort ||
(self.ImageBitmap && x instanceof ImageBitmap)
)
);
})
.catch((f) => {
(self as any).postMessage(['r', f, e.data[0], 1]);
});
};
}})()`,
],
{ type: 'text/javascript' }
)
)
);
constructor() {
this.worker?.addEventListener('message', (e: MessageEvent) => {
if (e.data[0] === 'r') {
this.promises[e.data[2]][e.data[3]](e.data[1]);
delete this.promises[e.data[2]];
}
});
}
public run(...args: any) {
if (!this.worker) {
throw new Error('Worker is not active anymore');
}
return new Promise((resolve, reject) => {
this.promises[++this.taskId] = [resolve, reject];
const fn = args.shift();
this.worker?.postMessage(
[
this.taskId,
fn.toString(),
args.map((m: unknown) =>
typeof m === 'string' ? JSON.stringify(m) : m
),
],
[args].filter(
(x: unknown) =>
x instanceof ArrayBuffer ||
x instanceof MessagePort ||
(self.ImageBitmap && x instanceof ImageBitmap)
)
);
});
}
public terminate() {
this.worker?.terminate();
this.worker = null;
}
}
// Example
(async () => {
const thread1 = new Thread();
const thread2 = new Thread();
const getUser = async (username: string) => {
const url = `https://api.github.com/users/${username}`;
const res = await fetch(url);
const profile = await res.json();
return profile.name;
};
const username = await thread1.run(getUser, 'timvanscherpenzeel');
console.log(username); // -> Tim van Scherpenzeel
const getSum = (c: number, d: number) => {
const a = 5;
const b = 9;
return a + b + c + d;
};
const sum1 = await thread1.run(getSum, 10, 20);
const sum2 = await thread2.run(getSum, 100, 200);
console.log(sum1, sum2); // -> 44 314
thread1.terminate();
thread2.terminate();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment