Atila
I'm on a mission to make code simple. When not recording screencasts or courses, you may find me either writing and talking about jamstack, performance, or developer tooling.
Lead Web Developer @ SAP
multithreading
paralellism
โ
single thread
multi threads
vs
concurrency
parallelism
vs
one at a time
from discord.com
simultaneous
from giphy.com
parallelism is
a special kind of
async
โฐ setTimeout
โฑ setInterval
๐ promise
๐ช fetch
async
await
โน mdn.io/web-workers
Web-Worker tasks do not interfere with the UI
๐ bye bye, Blocking Scripts
๐ฏ Dedicated Worker
๐ค Shared Worker
Dedicated to one main script
Shared by multiple main scripts
๐ฏ Dedicated Worker
๐ค Shared Worker
๐ช Main Thread
`window`
`DedicatedWorkerGlobalScope`
`SharedWorkerGlobalScope`
`self`
SharedWorkers API removed from Webkit
๐ซ transferred, not cloned
๐พ serialized objects
`worker.postMessage()`
๐ช different runtime
๐ก sends error event
`worker.onerror = () => {}`
๐ฎ only threadsafe DOM components
๐ data restrictions (only serialized objects)
โ own execution context
99%
of cases
add on
`worker.js`
request
origin is
`UID`
(universally unique identifier)
1%
of cases
๐ช different runtime
๐พ serialized objects
๐ฎ only threadsafe DOM components
๐ซ no DOM manipulation
cool, now gimme some
self.addEventListener( 'message', function (event) { self.postMessage(runBigTask(event.data, 'vanilla-worker')) }, false )
self.addEventListener( 'message', function (event) { self.postMessage(runBigTask(event.data, 'vanilla-worker')) }, false )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/vanilla-worker', import.meta.url), { name: 'vanilla-worker', type: 'module', } ) worker.postMessage(TASK_SIZE) worker.addEventListener('message', function (evt) { setData(evt.data) }) }} > Run Vanilla-WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/vanilla-worker', import.meta.url), { name: 'vanilla-worker', type: 'module', } ) worker.postMessage(TASK_SIZE) worker.addEventListener('message', function (evt) { setData(evt.data) }) }} > Run Vanilla-WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/vanilla-worker', import.meta.url), { name: 'vanilla-worker', type: 'module', } ) worker.postMessage(TASK_SIZE) worker.addEventListener('message', function (evt) { setData(evt.data) }) }} > Run Vanilla-WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/vanilla-worker', import.meta.url), { name: 'vanilla-worker', type: 'module', } ) worker.postMessage(TASK_SIZE) worker.addEventListener('message', function (evt) { setData(evt.data) }) }} > Run Vanilla-WebWorker </button> )
better
please
Proxy API
RPC โ `postMessage`
import { expose } from 'comlink' import { runBigTask } from './big-task' const worker = { runBigTask: async (int: number) => ( await runBigTask(int, 'comlink-worker') ), } export type ComlinkWorker = typeof worker expose(worker)
import { expose } from 'comlink' import { runBigTask } from './big-task' const worker = { runBigTask: async (int: number) => ( await runBigTask(int, 'comlink-worker') ), } export type ComlinkWorker = typeof worker expose(worker)
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/comlink-worker', import.meta.url), { name: 'runBigTaskWorker', type: 'module', } ) const { runBigTask } = wrap<ComlinkWorker>(worker) setData(await runBigTask(TASK_SIZE)) }} > Comlink WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/comlink-worker', import.meta.url), { name: 'runBigTaskWorker', type: 'module', } ) const { runBigTask } = wrap<ComlinkWorker>(worker) setData(await runBigTask(TASK_SIZE)) }} > Comlink WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/comlink-worker', import.meta.url), { name: 'runBigTaskWorker', type: 'module', } ) const { runBigTask } = wrap<ComlinkWorker>(worker) setData(await runBigTask(TASK_SIZE)) }} > Comlink WebWorker </button> )
export const Button = () => ( <button onClick={async () => { setData('loading') const worker = new Worker( new URL('../utils/comlink-worker', import.meta.url), { name: 'runBigTaskWorker', type: 'module', } ) const { runBigTask } = wrap<ComlinkWorker>(worker) setData(await runBigTask(TASK_SIZE)) }} > Comlink WebWorker </button> )
๐ subworkers
๐งโโ๏ธ
๐ฐ and other types of workers
`importScripts()`
getting โก๏ธout โก๏ธ of the main-thread is doable and cheap
By Atila
JavaScript is not single-threaded anymore. And it has been a while. But we are still leaving all the processing in the same thread that we render things. Let's have a look on how we can do it better. Unblock the main thread forever!!!
I'm on a mission to make code simple. When not recording screencasts or courses, you may find me either writing and talking about jamstack, performance, or developer tooling.