TL;DR: Parsing starts instantaneously after receiving the document.
Parsing and painting
For a more detailed explanation, we need to dive into the way rendering engines work.
Rendering engines parse the HTML document and create two trees: the content tree
and the render tree
. A content tree contains all DOM nodes. The render tree contains all styling information (the CSSOM
) and only the DOM nodes that are required to render te page.
As soon as the render tree has been created, the browsers goes through two processes: applying layout
and painting
each DOM node. Applying layout means calculating the exact coordinates where a DOM node should appear on the screen. Painting means actually rendering the pixels and applying stylistic properties.
This is a gradual process: browsers won't wait until all HTML is parsed. Parts of the content will be parsed and displayed, while the process continues with the rest of the contents that keeps coming from the network.
You can see this process happening in your browser. For example, open the Chrome Developer Tools and load a site of your choice.
After recording activity in the Network
tab, you'll notice that parsing starts while downloading the document. It recognises resources and starts downloading them. The blue vertical line indicates the DOMContentLoaded
event and the red vertical line indicates the load
event.
Recording a timeline gives you much more insight in what happens under the hood. I have included the screenshot above as an example to indicate that painting happens while parsing the document. Note that the initial paint occurs just before it continues parsing another part of the document. This process continues until it reaches the end of the document.
Single threaded
The rendering engine is single threaded. Almost everything, except network operations, happens in this thread.
Combine that with the synchronous nature of the web. Developers expect <script>
's to be parsed and executed immediately (that is: as soon as the parser reaches a script tag). That means that:
- The resource must be fetched from the network (this might be a slow process due to DNS lookups and speed of connection).
- The content of the resource is passed to the Javascript interpreter.
- The interpreter parses and executes the code.
Parsing the document halts until this process finishes. You don't improve the total parsing time by including <script>
's at the end of the document. It does enhance the user experience, as the process of parsing and painting isn't interrupted by <script>
's that need to be executed.
It is possible to work around this issue by marking the resource with defer
and / or async
. async
downloads the file during HTML parsing and will pause the HTML parser to execute it when it has finished downloading. defer
downloads the file during HTML parsing and will only execute it after the parser has completed.
Speculative parsing
Some browsers aim to work around the blocking aspect of <script>
's by using so called speculative parsing. The engine parses ahead (and runs the HTML tree construction!) while scripts are being downloaded and executed. Firefox and Chrome use this technique.
You can imagine the performance gain if a speculation succeeds (eg. the DOM wasn't altered by the scripts that are included in the document). Waiting for the scripts to execute wasn't necessary and the page has been painted successfully.
The downside is that there's more work lost when the speculation fails.
Luckily for us, very smart people work at these technologies, so even using document.write
properly won't break this process. Another rule of thumb is not to use document.write
. For example, it could break the speculative tree:
// Results in an unbalanced tree
<script>document.write("<div>");</script>
// Results in an unfinished token
<script>document.write("<div></div");</script>
Further reading
The following resources are worth your time reading: