I didn’t really read the spec though. The following are based on actual behaviors of Chrome (observed on Chromium 68, Ubuntu). Behaviors may vary among browsers, if they were just undefined in specifications. For example in 2010 scripts don’t always wait for proceeding stylesheets. I assume agreements had been achieved and behaviors had been standardized over the years.
The defer scripts are executed after domInteractive, before domContentLoaded; it’s sequential.
domInteractive and domContentLoaded are two timestamps which could be viewed in Chrome devtools’ Performance (previously Timeline) tab. Probably also in other similar tools, but I haven’t tried.
domInteractive is the point when HTML parsing and initial DOM construction are finished (and all “sync” scripts have finished executing). document.readyState changes from 'loading' to 'interactive'; a readystatechange event fires on document accordingly.
All defer scripts are executed in their appearing order. Then comes domContentLoaded, a DOMContentLoaded event fires on document.
DOM & CSSOM construction don’t rely on each other; but sync scripts may introduce dependencies.
Each sync script, internal or external, waits for preceding stylesheets to be parsed (of course, after fetched).
Yes, sync scripts are not blocked by subsequent stylesheets. MDN and Google and other articles say “scripts depend on CSSOM to be ready”; they (probably) didn’t mention that only preceding parts are depended.
P.S: Please not that google says that CSSOM is build before executing any inline javscript
Google didn’t say that (at least, as of the time I read this article).
On the contrary, before one sync script is fetched (if external) and executed, any code following it, HTML, stylesheets or other scripts, can’t be parsed/executed/constructed. They block anything subsequent to them.
So, in specific cases, eg. without sync scripts, DOMContentLoaded event may fire before or after CSSOM is ready. That’s what MDN means by saying “without waiting for stylesheets”.
defer/async scripts don’t care about stylesheets at all.
Different from sync scripts, defer/async scripts don’t wait for preceding stylesheets, and don’t block subsequent stylesheets/scripts either. They are removed from those “dependency chains” completely. You can’t rely on any proceeding stylesheets to have been parsed.
The differences between defer/async:
- as stated above,
deferscripts have predictable execution time; the DOM has been ready. They are also promised to execute in order.
Update:
deferscripts are added to the end of the list, says W3C’s spec (the 20th item)

(also in WHATWG’s spec) asyncscripts have no promise on execution order; eachasyncscript would be “queued to execute” as soon as it is fetched; once the render process is idle, they are executed. (To be exact, different types of resources have different priorities. The spec provides precious requirements)
These should well explain hinok’s two examples, the former async (from Google) and the latter defer。
I don’t have much experience on working with CSSOM at page loading (I do operate on DOM at page loading, though), so I can’t provide reliable advises. It seems “load event on window” or “force reflow early” might work.