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

  • 889