-
Notifications
You must be signed in to change notification settings - Fork 1.4k
docs: how to delay Qwik core execution #8041
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
base: main
Are you sure you want to change the base?
Conversation
|
built with Refined Cloudflare Pages Action⚡ Cloudflare Pages Deployment
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your help @GrandSchtroumpf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your help @GrandSchtroumpf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a couple of comments I will add later today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great, thank you for doing this!
|
||
If you want to squeeze the last bit of loadtime performance, you can delay its execution until you really need it. | ||
|
||
⚠️ Delaying core execution should **not** affect your UX or devX, this is an advance performance trick ⚠️ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the profiler on a small repro I have, it seems core is "woken up" eagerly anyways, likely because of modulepreload.
In any case, slow network delays are often orders of magnitude higher than slow CPU delays. On calibrated low tier CPU slowdown, there's some heavier js execution right after core is preloaded, but it's "just ~70ms (on a low tier device!), whereas on 3G the network delays can be counted in seconds.
So it's fine that core is woken up eagerly. In fact we preload it eagerly because it's 99% likely to be needed. Otherwise why use Qwik in the first place? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure to fully understand. There is a difference between preloading and executing.
- Preloading with modulepreload will fetch & compile in another thread
- Executing will fetch, compile & execute in the main thread
It's all a question of priority. If you don't execute core, you give room for the browser to do something else.
In any case core will always preload because we'll need it at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The important question is: what is actually leading to delays?
- Network delays
- blocking the main thread
If you run a minimal qwik app with just a counter, you'll see that even without clicking the counter, there is a main thread task coming from the core bundle right after it is preloaded.
But the most important thing here, is that the "blocking the main thread" is actually very little time. Network delays are 10x if not 100x bigger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did the tests, core is always preloaded :
- no hook: no execution at load time
useOn('qvisible', $(() => console.log('id'))
: no execution at load timeuseOnDocument('qidle', $(() => console.log('id'))
: module is evaluated (5µs on my computer)useOn('qvisible', $(() => console.log(state))
: core is executed (2ms on my computer)useVisibleTask$(() => console.log('id'))
: core is executed in 3 different tasks (2.5ms in total)
It's small numbers because it's on my machine & on a super small project, but it creates a small traffic on main thread, and if a dev wants to reduce it as much as possible I think he should know how to.
// Does not execute core at load time | ||
useOn('qidle', $(() => console.log(libId))); | ||
// Does not execute core at load time | ||
useOn('qidle', $(() => console.log('id'))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If core indeed executes eagerly then this is not true. Also with a current bug in the optizer output this is probably not deterministic. I would remove this section, it's not really actionable atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an actionable use case:
Let's say a dev read the doc and see that if he can he should replace useVisibleTask$
with useOn('qvisible')
. So he takes the effort to write this :
const ref = useState();
useOn('qvisible', $(() => ref.value.focus()));
The effort would have been for nothing, because ref
is in the scope of the component. With this doc, he now knows that this would not execute core :
useOn('qvisible', $(() => document.getElementById('id').focus()))
If hardcoding the id is no effort for him, this is a quick win :)
|
||
## useVisibleTask$ | ||
|
||
`useVisibleTask$` will **always** execute core before its callback is called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the problem with visible tasks is not that it wakes up core, but that if you have too many of them, you will have to preload all their bundles eagerly with 100% probability, preventing any user event from getting higher priority in the preload queue.
Some visible tasks are legitimate and must execute ASAP, but some could just be a useTask or useOn indeed.
I would put the emphasis on the fact that visible tasks tend to create preload traffic jams or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useOn('qvisible')
won't create the traffic jam useVisibleTask$
is doing ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm it should create the same traffic jam.
}); | ||
``` | ||
|
||
As we can see, this is a LOT of work, and it's not a great dev experience ! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. So what's the message here? That we should avoid this pattern? If so, we should remove this section. I remember an RFC to add media queries to useOnDocument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to explain what it requires to not execute core at load time. There are some valid cases for that though, if you're building a library to start listening on event from the browser like the example. There is no reason to execute core just to start an event listener.
The promise of Qwik is to optimize network & CPU usage at scale. A good part of the dev who use Qwik are looking to squeeze performance as much as possible, and delaying core execution is one tool. The devX is terrible, but I think the doc should mention how to achieve it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we can add this temporarily until the the media queries can be used with useOnDocument
. It will be much simpler. Can you add a link to the RFC here? Once the PR is merged we should also create an issue to remove it once the RFC is completed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
media query is just an example, any event listener that happen only on the browser could leverage this pattern :
- navigation.addEventListener("navigate", ...);
- navigator.connection.addEventListener('change', ...);
- navigator.devicePosture.addEventListener("change", ...);
...
They won't all fit into useOnDocument
. Also, I didn't find the RFC you mentioned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rules are as follows:
Waking up means executing the core module and deserializing the required state.
The only way core can wake up is via an event that is processed by qwikloader. Qwikloader runs the qrl, which in turn imports core to schedule or to retrieve captured scope.
Sync qrls never wake up core.
Regular qrls that capture scope always wake up core, for deserializing.
Tasks always wake up core, for scheduling.
UseOn* registers a qrl handler, no direct impact on core.
What is it?
Description
Add documentation about waking up core
Checklist
pnpm change