Last active
October 14, 2020 12:07
-
-
Save TimvanScherpenzeel/d0035cf04e8224da759611fa36c93a64 to your computer and use it in GitHub Desktop.
A re-usable thread implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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