next.js
bf23890a - [Cache Components] Fast setImmediate (#86018)

Commit
28 days ago
[Cache Components] Fast setImmediate (#86018) > This place is not a place of honor... no highly esteemed deed is commemorated here... nothing valued is here. > What is here was dangerous and repulsive to us. This message is a warning about danger. you think you know when setImmediate is supposed to run? no you don't --- This PR introduces a patch to the Node `setImmediate` builtin. The patch is enabled by calling `DANGEROUSLY_runPendingImmediatesAfterCurrentTask()`. All immediates scheduled after that point (until the end of the task) will be captured and executed right after that task (after `process.nextTick` and `microtasks`). This applies to immediates scheduled from immediates as well. This is relevant when scheduling back-to-back timeouts for staged rendering in Cache Components: ```ts setTimeout(() => { // runs first DANGEROUSLY_runPendingImmediatesAfterCurrentTask() // enable the patch setImmediate(() => { // runs second (normally, it'd run last!) }) }) setTimeout(() => { // runs third }) ``` the immediate scheduled from inside the first timeout will **always** run before the second timeout. A side-effect of this is that `setImmediate` will no longer be considered IO in the Cache Components rendering model, because immediates will always run before we advance the stage (or abort a prerender), and thus can't result in a dynamic hole. This brings the runtime behavior in line with React Devtools, which does not show `setImmediate` as IO. This patch also has some observable differences in behavior from native `setImmediate`, mostly to do with uncaught errors: 1. sync errors in `process.nextTick` no longer interrupt `processTicksAndRejections` (which would make us move onto the next event loop step, and run the rest of the nextTick queue after the next task, breaking our scheduling). They're rethrown in a microtask, which changes the timing of `uncaughtException` a bit. 2. unhandled rejections will trigger `unhandledRejection` after _all_ fast immediates are done executing, not after each immediate. This happens because our userspace immediate scheduling relies on nextTick, and [rejections are only processed after everything else in `processTicksAndRejections` is done](https://github.com/nodejs/node/blob/d546e7fd0bc3cbb4bcc2baae6f3aa44d2e81a413/lib/internal/process/task_queues.js#L104-L105). We hope that these divergences in behavior are niche enough to not affect any real world code. Both are potentially fixable by managing our own nextTick queue, more, but we'll try to avoid that complexity for now.
Author
Parents
Loading