Skip to content

Conversation

GrandSchtroumpf
Copy link
Contributor

What is it?

  • Docs

Description

Add documentation about waking up core

Checklist

  • My code follows the developer guidelines of this project
  • I performed a self-review of my own code
  • I added a changeset with pnpm change
  • I made corresponding changes to the Qwik docs
  • I added new tests to cover the fix / functionality

@GrandSchtroumpf GrandSchtroumpf requested a review from a team as a code owner October 7, 2025 16:02
Copy link

changeset-bot bot commented Oct 7, 2025

⚠️ No Changeset found

Latest commit: 18c5aed

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

github-actions bot commented Oct 7, 2025

built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview 18c5aed

Copy link
Member

@gioboa gioboa left a 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

@gioboa gioboa requested review from maiieul and wmertens October 7, 2025 17:40
@gioboa gioboa changed the title Load core docs: how to delay Qwik core execution Oct 7, 2025
gioboa
gioboa previously approved these changes Oct 8, 2025
Copy link
Member

@gioboa gioboa left a 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

Copy link
Contributor

@maiieul maiieul left a 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.

Copy link
Member

@wmertens wmertens left a 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 ⚠️
Copy link
Contributor

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? 😄

Copy link
Contributor Author

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.

Copy link
Contributor

@maiieul maiieul Oct 10, 2025

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?

  1. Network delays
  2. 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.

Copy link
Contributor Author

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 time
  • useOnDocument('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')));
Copy link
Contributor

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.

Copy link
Contributor Author

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.
Copy link
Contributor

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.

Copy link
Contributor Author

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 ?

Copy link
Contributor

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 !
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@GrandSchtroumpf GrandSchtroumpf Oct 10, 2025

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.

Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants