-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should two different defer scripts execute in the same task? #6230
Comments
I don't see anything in the spec that says each script runs in its own task. https://html.spec.whatwg.org/multipage/scripting.html#the-script-element Using |
Per current texts, UAs should "spin the event loop" while waiting for the first script in the list to be ready. Usually "spin the event loop" should allow for rendering updates, but here it's quite unclear if UAs actually are forced to enter the "spin the event loop" algorithm since when script one execution is done, script two and three already have their "ready to be parser-executed" flags set since about two and four full seconds respectively. |
Which is not ideal for spec text, right? :)
I thought about this but need to confirm that the order of execution is still guaranteed. From timer initialization steps, I think step 16 gives that guarantee. "Wait until any invocations of this algorithm that had the same method context, that started before this one, and whose timeout is equal to or less than this one's, have completed." |
Nice investigation @Kaiido! Yeah this is tricky. Per spec, we spin the event loop until condition goal happens, and I think a strict interpretation of the spec here always has us invoking spin the event loop ; its just that by the time we actually check goal, we're already in parallel. When goal is already met, we immediately post the task to come back, but it is still async with respect to the "main thread". I think the next step would be to figure out what all browsers do here and see how it compares to the spec. We can can either get them to converge or change the spec (although it sounds like the spec's behavior may be ideal, albeit implicit). @rik would you be able to investigate into what other browsers are doing here? Maybe a test like your Glitch demo would work to collect this data. As we discussed Chrome will execute all "ready" deferred scripts in one task as per https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/script/html_parser_script_runner.cc;l=472;drc=50cb1c9e9c1eb0cc9e42fd5519d44e3401116ac3 but there was some talk a while back about changing this, it's just a matter of project priority. |
@domfarolino Sorry I should have included this info in my original report. On my Mac, Safari 14.0.2, Edge 87.0.664.66 and Firefox 84.0.1 all scripts seem to execute in the same task. |
AFAIU "spin the event loop" is like an async/await and always posts a task when it's done to continue. It means that currently the spec actually separates the deferred scripts by one per task, but the implementations do not. @domenic maybe has some historical context for this? |
Tracking this in chrome: https://bugs.chromium.org/p/chromium/issues/detail?id=1418367 |
I don't know its context, but I think this is one of many code paths around parsing end that are sync in Chromium but should be async in the spec. Actually, the entire https://html.spec.whatwg.org/multipage/parsing.html#stop-parsing can be executed in a single task in Chromium (e.g. https://bugs.chromium.org/p/chromium/issues/detail?id=961428). Code change in Chromium to make this spec conformant would be simple. It might cause performance regressions due to additional async tasks (and performance improvements by avoiding long tasks) and might break websites in the wild (there are many websites that depend on this kind of Chromium's sync behavior despite not spec conformant), but probably they are not big blockers. BTW in another similar spin-the-event-loop usage https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incdata:spin-the-event-loop, we don't enter spin-the-event-loop if the condition is already met. |
I'm not sure websites can rely on the sync behaviour since the grouping in one long task is dependant on a race condition. |
The spec matches what is implemented as described here in that "spin the event loop" doesn't invoke "update the rendering". Defer scripts are run during "the end" which runs spin the event loop between each defer/module script. So I'm not sure if there are reasons for this, it seems reasonable to allow rendering updates between scripts. |
Ah, thanks for the clarification
A plausible reason would be to prevent flashing as well as reducing redundant layouts based on partial state. |
I don't follow why "update the rendering" would have to be called explicitely. By queuing a task (6.2) you're basically enabling the whole event loop iteration to be processed, and you have to go through "update the rendering" before your task is executed (moreover if you expect a timer task to fill in between). |
I'm confused. You're saying the spec would run setTimeout() in between defer scripts, but isn't the OP about the implementation not doing that, i.e., they run in the same task? Doesn't that mean the spec does not match what is implemented? |
@domfarolino the glitch demo only tests changing styles, so I commented on why that doesn't happen per spec. I haven't tested what browsers do for setTimeout. @Kaiido https://html.spec.whatwg.org/multipage/webappapis.html#spin-the-event-loop (called between defer scripts) is different from normal processing of the event loop https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model |
Could you clarify how it is different when we're between defer scripts? |
Hmm, reading the algorithm for spin the event loop again I think I had the wrong idea of how it works. Indeed it doesn't have a loop to run tasks and check goal, it just waits in parallel until goal is met. So the normal event loop processing must happen in the meantime. Apologies for the confusion. |
So we're all aligned that the spec as-is will not run defer scripts in the same task, and between the execution of two defer scripts we fully defer to the event loop which may run other queued tasks and indeed update the rendering? |
Yes! Though rendering opportunity is implementation-specific and mentions "user agent throttling for performance reasons". Throttling rendering due to having pending scripts would be per-spec. |
With the following HTML
If
short-download.js
has finished being fetched beforelong-download.js
then both will execute in the same task.This is visible in a Glitch demo (with 3 scripts in the same task) Glitch project to remix
Use case
While trying to split our code into several smaller scripts to prevent long blocking tasks, we noticed some defer scripts would sometime run in the same task, defeating the purpose of our "optimisation". The "sometime" was the race condition described here (that can happen because of network conditions or caches).
(conversation started in a Twitter thread with @jakearchibald)
The text was updated successfully, but these errors were encountered: