Multithread Demystified

October / 2021

Atila Fassina

Lead Web Developer @ SAP

 Hallo vom Berlin

multithreading

paralellism

single thread

multi threads

vs

concurrency

parallelism

vs

concurrent threads

one at a time

from discord.com

paralell threads

simultaneous

from giphy.com

parallelism is

a special kind of

 

async

let’s talk about JavaScript

Concurrent JS APIs

⏰ setTimeout

⏱ setInterval

🙏 promise

🪃 fetch

     async

     await

what about paralell?!

web

workers

mdn.io/web-workers

UX 💛 Web-Workers

Web-Worker tasks do not interfere with the UI

👋 bye bye, Blocking Scripts

Web-Workers types

🎯 Dedicated Worker

🤝 Shared Worker

Dedicated to one main script

Shared by multiple main scripts

Scopes

🎯 Dedicated Worker

🤝 Shared Worker

🪟 Main Thread

`window`
`DedicatedWorkerGlobalScope`
`SharedWorkerGlobalScope`
`self`

SharedWorkers API removed from Webkit

caveats

🪤

data

📦

📫 transferred, not cloned

👾 serialized objects

`worker.postMessage()`

errors

💣

🪐 different runtime

📡 sends error event

`worker.onerror = () => {}`

Thread safety

🚔

👮 only threadsafe DOM components

🔒 data restrictions (only serialized objects)

❌ own execution context

CSP Header

🔐

add on

`worker.js`

 request

99%

of cases

 origin is

`UID`

(universally unique identifier)

1%

of cases

DOM

🖥

🪐 different runtime

👾 serialized objects

👮 only threadsafe DOM components

🚫 no DOM manipulation

React

cool, now gimme some

sync

Promise

Worker

self.addEventListener(
  'message',
  function (event) {
    self.postMessage(runBigTask(event.data, 'vanilla-worker'))
  },
  false
)

worker.js

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>
)

trigger Worker task

DX

better

please

Comlink

background-threads

like it’s main

📡

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)

comlink.js

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>
)

trigger Worker task

what more?

🐝 subworkers

🧞‍♀️

🛰 and other types of workers

`importScripts()`

🎁

takeaway

🥡

getting ⚡️out ⚡️ of the main-thread is doable and cheap

thank you

Multi-Thread Demystified

By Atila

Multi-Thread Demystified

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!!!

  • 775