├── EXPLAINER.md └── README.md /EXPLAINER.md: -------------------------------------------------------------------------------- 1 | # Current solutions 2 | It seems that several JS libraries already exist that offer satisfactory worker DOM implementations. [Virtual DOM](https://github.com/Matt-Esch/virtual-dom) and React's render model both seem effective. However, as adoption of these libraries increase, it may be worthwhile to 'pave the cow path' and build these constructs into the platform. 3 | 4 | # `WorkerNode` 5 | This proposal specifies a new immutable object model for use inside workers or on the main thread. The objects provide the instructions for how to build a DOM subtree and how to inflate that subtree into a real DOM subtree. `WorkerNode` is intended to work in tandem with [async append](https://github.com/drufball/async-append). 6 | 7 | This has advantages over postMessage and innerHTML because the engine knows intent when creating a CompactNode tree and can pre-allocate data structures that the elements will eventually be inflated into. There are also ergonomic benefits to using nodes and tree structures that developers are already familiar with. 8 | 9 | This proposal uses JS objects to represent DOM, which incurs some overhead from binding costs. In our experience analyzing pages, the overhead of bindings is negligible. The increased ergonomics from using familiar DOM manipulation paradigms greatly outweighs any small performance impact from bindings. 10 | 11 | ## API 12 | __Note: This API is very much a rough draft / strawman. Suggestions for improvement welcome.__ 13 | 14 | ```idl 15 | [Exposed=(Window,Worker)] 16 | interface CompactNode { }; 17 | 18 | [NoInterfaceObject] 19 | interface CompactParentNode extends CompactNode { 20 | readonly attribute sequence childNodes; 21 | }; 22 | 23 | [Constructor(String name, String value), 24 | Exposed=(Window,Worker)] 25 | interface CompactAttr { 26 | readonly attribute String name; 27 | readonly attribute String value; 28 | }; 29 | 30 | [Constructor(String tagName, sequence attributes, sequence childNodes, sequence shadowRoots), 31 | Exposed=(Window, Worker)]; 32 | interface CompactElement extends CompactParentNode { 33 | readonly attribute String tagName; 34 | readonly attribute sequence attributes; 35 | }; 36 | 37 | [Constructor(sequence childNodes), 38 | Exposed=(Window, Worker)]; 39 | interface CompactShadowRoot extends CompactParentNode {}; 40 | 41 | [Constructor(String data), 42 | Exposed=(Window, Worker)] 43 | interface CompactText extends CompactNode { 44 | readonly attribute data; 45 | }; 46 | 47 | typedef (NodeInflationRequest or CompactNode) NodeInflationInfo; 48 | 49 | [Constructor(CompactNode node), 50 | Exposed=Window] 51 | interface NodeInflationRequest { 52 | readonly attribute CompactNode node; 53 | void cancel(); // Cancelable promises? 54 | }; 55 | 56 | partial interface Node { 57 | static Promise inflate(NodeInflationInfo target); 58 | }; 59 | ``` 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # worker-node 2 | Allowing DOM operations on worker threads 3 | 4 | # Problem 5 | In order to meet the [RAIL performance model](https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail?hl=en), sites must respond to user input within __100ms__. For large, complex web apps this means that every ms of work counts. If the main thread isn't free within 100ms, the user will experience jank. 6 | 7 | The two methods of protecting the main thread are to chunk work into small pieces or to offload large tasks to separate threads. When working with DOM, though, neither of these approaches is feasible. The rendering lifecycle executes as a monolithic chunk, easily costing __30-60ms__ each time a new layout is required. [async-append](https://github.com/drufball/async-append) is a proposal to make the rendering lifecycle chunkable to avoid this. 8 | 9 | In addition to the rendering lifecycle, there is often a lot of JS work required to calculate what DOM mutations are necessary. This is especially true of large apps which have thousands of elements on the page. Because DOM can only be created/manipulated on the main thread, it is difficult to calculate DOM changes without also doing it on the main thread. 10 | 11 | # Worker DOM 12 | In order to allow developers to move their long-running DOM manipulation code off the main thread, we need to allow them to interact with DOM in workers. This is more complex than it may seem at first. Certain DOM operations, such as reading `height` and `width` attributes, requires a new layout which can only be done on the main thread. For more explorations of potential solutions, see the [EXPLAINER](EXPLAINER.md). 13 | 14 | There are several existing JS libraries that offer [virtual DOM](https://github.com/Matt-Esch/virtual-dom), a lightweight JS representation of DOM that can be manipulated from workers. These frameworks get around the layout issue mentioned above by restricting what actions are allowed in workers. Essentially, only operations that do not require a layout are allowed. 15 | --------------------------------------------------------------------------------