Slide 1

Slide 1 text

JavaScript, Concurrency, and the DOM Malte Ubl and Kristofer Baxter

Slide 2

Slide 2 text

Concurrency is a technical word for multiple parts of a computer program running at the same time. If your computer/phone has more than one core (it does) or you have more than one computer (the cloud does) then this can make things get done much faster. It generally makes things more complicated

Slide 3

Slide 3 text

Threads Programs that run on a computer Photo: D-BASE, Getty Images, DigitalVision

Slide 4

Slide 4 text

Single-threaded Application has only 
 one thread Photo: D-BASE, Getty Images, DigitalVision

Slide 5

Slide 5 text

Traditionally web browsers have only a single thread. The main thread. Your JavaScript runs on that thread. If your JavaScript runs and until it is done running, your browser cannot do anything else.

Slide 6

Slide 6 text

OMG, 60 frames 
 per second

Slide 7

Slide 7 text

OMG, 60 frames 
 per second JANK

Slide 8

Slide 8 text

You have 16ms to update the screen, right?

Slide 9

Slide 9 text

You, layout, style recalc, paint, composite, actual physical pixel flip

Slide 10

Slide 10 text

0 16 Browser You: Developer JavaScript 8

Slide 11

Slide 11 text

0 8 You: Developer JavaScript Event Processing Determine New State State Change Processing Issue DOM Updates click

Slide 12

Slide 12 text

0 10 Developer JavaScript Event Processing Determine New State State Change Processing Issue DOM Updates click

Slide 13

Slide 13 text

0 18 Browser You: Developer JavaScript 10 JANK

Slide 14

Slide 14 text

It’s common to exceed your budget. Don’t feel bad! We all do it!

Slide 15

Slide 15 text

Why?

Slide 16

Slide 16 text

Not All Mobile Devices Are Created Equal

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

0 100 Brand New Devices Categorized by Price Segments

Slide 19

Slide 19 text

15th Percentile ($80) 75th Percentile ($600) 90th Percentile ($1000) 0 100

Slide 20

Slide 20 text

0 1000 2000 3000 4000 5000 2011 2013 2016 2018 Mobile Device Single Core Performance Over Time iPhone X Pixel Nokia 2 Samsung J3 Remember the Performance Gap

Slide 21

Slide 21 text

What’s Multicore Performance Like?

Slide 22

Slide 22 text

0 1000 2000 3000 4000 5000 2011 2013 2016 2018 Low End Devices Multi Core Performance Over Time Samsung J3 Nokia 2

Slide 23

Slide 23 text

Seriously, it can’t be that bad right? Oh, it is.

Slide 24

Slide 24 text

Video Showing Ares-6 Benchmark on iPhone X versus Nokia 2

Slide 25

Slide 25 text

OK, we need some concurrency in our JavaScript

Slide 26

Slide 26 text

Introducing a super amazing new API … Web Workers! Photo: JStevensJr, Getty Images, Creatas Video+ / Getty Images Plus

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Web Workers A way to do multi-threading in JavaScript in web browsers. Workers share no state with each other or the main-thread. Workers only have access to a very limited set of web APIs. In particular, they do not have access to the DOM

Slide 29

Slide 29 text

const worker = new Worker('worker.js'); const inputEl = document.createElement(‘input'); inputEl.onchange = _ => { worker.postMessage({value: inputEl.value}); console.log(`value, ${inputEl.value} passed to worker`); }; document.body.appendChild(inputEl);

Slide 30

Slide 30 text

const worker = new Worker('worker.js'); const inputEl = document.createElement(‘input'); inputEl.onchange = _ => { worker.postMessage({value: inputEl.value}); console.log(`value, ${inputEl.value} passed to worker`); }; document.body.appendChild(inputEl); worker.onmessage = e => { console.log(`result, ${e.data} received from worker thread’); }; Main Thread self.onmessage = e => { console.log(`value, ${e.data} received from main thread`); const result = `Result: ${e.data.value}`; console.log(`result, ${result} passed to main thread`); postMessage(result); } Worker Thread change postMessage onMessage onMessage postMessage

Slide 31

Slide 31 text

Low Level Primitive Is there something a bit more abstracted, perhaps usable?

Slide 32

Slide 32 text

(async function() { class MyRemoteClass { doExpensiveCalculation(a, b) { return a + b; } } const instance = await Clooney.spawn(MyRemoteClass); console.log(await instance.doExpensiveCalculation(5, 23)); })(); https://github.com/GoogleChromeLabs/clooney

Slide 33

Slide 33 text

const endpoint = 'https://endpoint.com/dogs/are/best”; const result = await worker{| const res = await fetch(endpoint); const json = await res.json(); return json[2].firstName; |}; https://github.com/domenic/proposal-blocks

Slide 34

Slide 34 text

https://github.com/developit/stockroom

Slide 35

Slide 35 text

I thought this talk was about the DOM? Yes, you’re right of course.

Slide 36

Slide 36 text

WorkerDOM The same DOM API and Frameworks you know, but in a Web Worker.

Slide 37

Slide 37 text

Original Requirements “Hey Kris, make it so that React works in a Web Worker.” That is actually the full list. But then we realized we could be a bit more abstract.

Slide 38

Slide 38 text

Makes the full[1] DOM available to your Web Worker. Effectively this means you can run your existing web app in a Web Worker. And It Just Works™ [1] [1] Terms and Conditions apply.

Slide 39

Slide 39 text

Use Cases Speed up your existing app. Support framework authors in speeding up their existing framework. Isolate performance impact of third party scripts on your app.

Slide 40

Slide 40 text

Event Processing Determine New State State Change Processing Issue DOM Updates 10 Browser 18 Main Thread Time on main thread: 18ms / janky Time until event processed: 18ms

Slide 41

Slide 41 text

Event Processing Determine New State State Change Processing Issue DOM Updates 2 Browser 10 Main Thread Background Thread Time on main thread: 10ms / silky smooth Time until event processed: 18ms (latency unchanged)

Slide 42

Slide 42 text

Limitations Some DOM APIs aren’t yet implemented. A small number of DOM APIs cannot be implemented and alternatives are provided. A smaller number of DOM APIs cannot be implemented and no alternative can be provided.

Slide 43

Slide 43 text

Unimplementable with alternative Primarily DOM APIs that provide synchronous access to computed layout. Think getBoundingClientRect and getComputedStyle Alternatives are provided with Async suffix like getBoundingClientRectAsync which return a promise. Side benefit: This automatically prevents performance red flags like “double sync layout in a single frame”.

Slide 44

Slide 44 text

Unimplementable without alternative Synchronous methods on Event like preventDefault and stopPropagation. Transferable events may come to browsers soon which would make this work. Until then: If you need these methods, you may need to run them manually on the main thread.

Slide 45

Slide 45 text

How does this work? No need to understand the guts to use it, but it is fun stuff.

Slide 46

Slide 46 text

Main Thread Worker Thread UpgradedElement HTMLDivElement WorkerDOM Document HTMLDivElement Runtime Hydration Mutation Upgrade Element Click Handler Mouse Handler

Slide 47

Slide 47 text

Hydration Compare and Upgrade String Markup Into Usable DOM Nodes Photo: Claudia Miranda / EyeEm, Getty Images, EyeEm

Slide 48

Slide 48 text

spaner Hello World!
HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement

Slide 49

Slide 49 text

spaner Hello World!
HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement

Slide 50

Slide 50 text

export interface HydrateableNode { readonly nodeType: NodeType; // NodeType.ELEMENT_NODE readonly nodeName: string; // "div" readonly _index_: number; // 3 readonly transferred: boolean; // false readonly attributes?: Array; // [] readonly properties?: Array; // [] readonly childNodes?: Array; // [...] readonly textContent?: string; // undefined readonly namespaceURI?: string; // undefined } spaner Hello World!
HydrateableNode HydrateableNode HydrateableNode HydrateableNode HydrateableNode HydrateableNode export interface HydrateableNode { readonly nodeType: NodeType; readonly nodeName: string; readonly _index_: number; readonly transferred: boolean; readonly attributes?: Array; readonly properties?: Array; readonly childNodes?: Array; readonly textContent?: string; readonly namespaceURI?: string; } HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement

Slide 51

Slide 51 text

{ "nodeType": 1, "nodeName": "section", "childNodes": [ { "nodeType": 1, "nodeName": "div", "childNodes": [...], "_index_": 5, "transferred": false } ], "_index_": 4, "transferred": false, } [“nodeType”, “nodeName”, “childNodes”, “_index_”, “transferred”] => [0, 1, 4, 7, 8]

Slide 52

Slide 52 text

{ "nodeType": 1, "nodeName": "section", "childNodes": [ { "nodeType": 1, "nodeName": "div", "childNodes": [...], "_index_": 5, "transferred": false } ], "_index_": 4, "transferred": false, } { 0: 1, 1: "section", 4: [ { 0: 1, 1: "div", 4: [...], 7: 5, 8: false } ], 7: 4, 8: false, } [“nodeType”, “nodeName”, “childNodes”, “_index_”, “transferred”] => [0, 1, 4, 7, 8]

Slide 53

Slide 53 text

{ "nodeType": 1, "nodeName": "section", "childNodes": [ { "nodeType": 1, "nodeName": "div", "childNodes": [...], "_index_": 5, "transferred": false } ], "_index_": 4, "transferred": false, } [false, true] => [0, 1] { 0: 1, 1: "section", 4: [ { 0: 1, 1: "div", 4: [...], 7: 5, 8: 0 } ], 7: 4, 8: 0, }

Slide 54

Slide 54 text

{ "nodeType": 1, "nodeName": "section", "childNodes": [ { "nodeType": 1, "nodeName": "div", "childNodes": [...], "_index_": 5, "transferred": false } ], "_index_": 4, "transferred": false, } { 0: 1, 1: 1, 4: [ { 0: 1, 1: 2, 4: [...], 7: 5, 8: 0 } ], 7: 4, 8: 0, } [“section”, “div”] => [1, 2]

Slide 55

Slide 55 text

export interface HydrationFromWorker { readonly type: MessageType.HYDRATE; readonly strings: Array; readonly nodes: HydrateableNode; readonly addedEvents: Array; } { 9: 2, 39: ["#document", "body", "div", "#text", "Hello World!", …], 35: {…}, 20: [], }

Slide 56

Slide 56 text

Mutation A Static Document Would be Quite Boring

Slide 57

Slide 57 text

const span = document.createElement('span'); span.addEventListener('click', (e) => { span.classList.toggle('clicked'); div.style.color = div.style.color === “green” ? "red" : "green"; });

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Does it actually work? Demo Time

Slide 60

Slide 60 text

Can I try this? worker-dom became available on GitHub and npm a few minutes ago. It is still very alpha. If you maintain a framework or build tool, we’d love to work with you to make sure it works with WorkerDOM!

Slide 61

Slide 61 text

Why we are building this WorkerDOM will allow authors of AMP documents to run their own JavaScript. WorkerDOM is alpha today, we will make it rock-solid.

Slide 62

Slide 62 text

Thank you! GitHub: bit.ly/worker-dom Blog post: bit.ly/worker-dom-blog
 @kristoferbaxter & @cramforce