WorkerDOM: JavaScript Concurrency and the DOM

Ed4279256911f600867ed4519ec5c945?s=47 Malte Ubl
August 21, 2018

WorkerDOM: JavaScript Concurrency and the DOM

Ed4279256911f600867ed4519ec5c945?s=128

Malte Ubl

August 21, 2018
Tweet

Transcript

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

  2. 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
  3. Threads Programs that run on a computer Photo: D-BASE, Getty

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

    Images, DigitalVision
  5. 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.
  6. OMG, 60 frames 
 per second

  7. OMG, 60 frames 
 per second JANK

  8. You have 16ms to update the screen, right?

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

  10. 0 16 Browser You: Developer JavaScript 8

  11. 0 8 You: Developer JavaScript Event Processing Determine New State

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

    Change Processing Issue DOM Updates click
  13. 0 18 Browser You: Developer JavaScript 10 JANK

  14. It’s common to exceed your budget. Don’t feel bad! We

    all do it!
  15. Why?

  16. Not All Mobile Devices Are Created Equal

  17. None
  18. 0 100 Brand New Devices Categorized by Price Segments

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

    100
  20. 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
  21. What’s Multicore Performance Like?

  22. 0 1000 2000 3000 4000 5000 2011 2013 2016 2018

    Low End Devices Multi Core Performance Over Time Samsung J3 Nokia 2
  23. Seriously, it can’t be that bad right? Oh, it is.

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

  25. OK, we need some concurrency in our JavaScript

  26. Introducing a super amazing new API … Web Workers! Photo:

    JStevensJr, Getty Images, Creatas Video+ / Getty Images Plus
  27. None
  28. 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
  29. 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);
  30. 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
  31. Low Level Primitive Is there something a bit more abstracted,

    perhaps usable?
  32. <script src="/clooney.bundle.js"></script> <script> (async function() { class MyRemoteClass { doExpensiveCalculation(a,

    b) { return a + b; } } const instance = await Clooney.spawn(MyRemoteClass); console.log(await instance.doExpensiveCalculation(5, 23)); })(); </script> https://github.com/GoogleChromeLabs/clooney
  33. const endpoint = 'https://endpoint.com/dogs/are/best”; const result = await worker<endpoint>{| const

    res = await fetch(endpoint); const json = await res.json(); return json[2].firstName; |}; https://github.com/domenic/proposal-blocks
  34. https://github.com/developit/stockroom

  35. I thought this talk was about the DOM? Yes, you’re

    right of course.
  36. WorkerDOM The same DOM API and Frameworks you know, but

    in a Web Worker.
  37. 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.
  38. 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.
  39. 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.
  40. 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
  41. 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)
  42. 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.
  43. 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”.
  44. 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.
  45. How does this work? No need to understand the guts

    to use it, but it is fun stuff.
  46. Main Thread Worker Thread UpgradedElement HTMLDivElement WorkerDOM Document HTMLDivElement Runtime

    Hydration Mutation Upgrade Element Click Handler Mouse Handler
  47. Hydration Compare and Upgrade String Markup Into Usable DOM Nodes

    Photo: Claudia Miranda / EyeEm, Getty Images, EyeEm
  48. </section> </div> <input /> <span>spaner</span> Hello World! <div> <section src="vanilla.js">

    HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement
  49. </section> </div> <input /> <span>spaner</span> Hello World! <div> <section src="vanilla.js">

    HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement
  50. export interface HydrateableNode { readonly nodeType: NodeType; // NodeType.ELEMENT_NODE readonly

    nodeName: string; // "div" readonly _index_: number; // 3 readonly transferred: boolean; // false readonly attributes?: Array<Attribute>; // [] readonly properties?: Array<Property>; // [] readonly childNodes?: Array<HydrateableNode>; // [...] readonly textContent?: string; // undefined readonly namespaceURI?: string; // undefined } </section> </div> <input /> <span>spaner</span> Hello World! <div> <section src="vanilla.js"> HydrateableNode HydrateableNode HydrateableNode HydrateableNode HydrateableNode HydrateableNode export interface HydrateableNode { readonly nodeType: NodeType; readonly nodeName: string; readonly _index_: number; readonly transferred: boolean; readonly attributes?: Array<Attribute>; readonly properties?: Array<Property>; readonly childNodes?: Array<HydrateableNode>; readonly textContent?: string; readonly namespaceURI?: string; } HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement
  51. { "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]
  52. { "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]
  53. { "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, }
  54. { "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]
  55. export interface HydrationFromWorker { readonly type: MessageType.HYDRATE; readonly strings: Array<string>;

    readonly nodes: HydrateableNode; readonly addedEvents: Array<EventSubscriptionChange>; } { 9: 2, 39: ["#document", "body", "div", "#text", "Hello World!", …], 35: {…}, 20: [], }
  56. Mutation A Static Document Would be Quite Boring

  57. const span = document.createElement('span'); span.addEventListener('click', (e) => { span.classList.toggle('clicked'); div.style.color

    = div.style.color === “green” ? "red" : "green"; });
  58. None
  59. Does it actually work? Demo Time

  60. 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!
  61. 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.
  62. Thank you! GitHub: bit.ly/worker-dom Blog post: bit.ly/worker-dom-blog
 @kristoferbaxter & @cramforce