Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WorkerDOM: JavaScript Concurrency and the DOM

Malte Ubl
August 21, 2018

WorkerDOM: JavaScript Concurrency and the DOM

Malte Ubl

August 21, 2018
Tweet

Other Decks in Programming

Transcript

  1. 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
  2. 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.
  3. 0 8 You: Developer JavaScript Event Processing Determine New State

    State Change Processing Issue DOM Updates click
  4. 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
  5. 0 1000 2000 3000 4000 5000 2011 2013 2016 2018

    Low End Devices Multi Core Performance Over Time Samsung J3 Nokia 2
  6. Introducing a super amazing new API … Web Workers! Photo:

    JStevensJr, Getty Images, Creatas Video+ / Getty Images Plus
  7. 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
  8. 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);
  9. 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
  10. <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
  11. 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
  12. 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.
  13. 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.
  14. 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.
  15. 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
  16. 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)
  17. 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.
  18. 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”.
  19. 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.
  20. How does this work? No need to understand the guts

    to use it, but it is fun stuff.
  21. Hydration Compare and Upgrade String Markup Into Usable DOM Nodes

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

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

    HTMLSectionElement HTMLDivElement Text HTMLSpanElement Text HTMLInputElement
  24. 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
  25. { "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]
  26. { "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]
  27. { "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, }
  28. { "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]
  29. 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: [], }
  30. 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!
  31. 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.