We all know what `defer` attribute on <script> tag means.
"the script is downloaded in parallel to parsing the page, and executed after the page has finished parsing".
So we naively place this attribute on non-critical scripts.
And this might be a problem 👇 (1/6)
#webperf
What we don't know (or at least I didn't until @nomsternom explained it to me) is this:
When the page has been parsed, ALL fully downloaded deferred scripts are executed in a batch, i.e. as a single task.
(2/6)
While each one of these scripts alone may execute very fast, when executed in a batch, they can take up significant amount of time when executed as a single batch task. And this task blocks the main thread, thus delaying interactivity and hurting user experience. (3/6)
One way to solve this is by using dynamic imports for loading non-critical scripts. This way they won't block the parsing and also won't be batched.
However, this is not always possible, for example if the scripts are a UMD bundles or just separate deployables.
(4/6)
In this case what you can do is put this script with `async` attribute at the end of <body> tag. Since it's not deferred it won't be batched. Since it's `async` and is placed at the end of body it won't block the parsing while downloaded or executed.
(5/6)
The only downside of this approach is that it will be downloaded pretty late, but hey, we're talking about non-critical scripts, so who cares, right?
(6/6)