Skip to content

Commit 06d3719

Browse files
committed
clean up CacheSignal changes
1 parent 921039d commit 06d3719

File tree

5 files changed

+31
-57
lines changed

5 files changed

+31
-57
lines changed

packages/next/src/server/app-render/app-render.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ import {
200200
trackPendingChunkLoad,
201201
trackPendingImport,
202202
trackPendingModules,
203-
trackPendingModulesInRender,
204203
} from './module-loading/track-module-loading.external'
205204
import { isReactLargeShellError } from './react-large-shell-error'
206205
import type { GlobalErrorComponent } from '../../client/components/builtin/global-error'
@@ -733,7 +732,7 @@ async function prospectiveRuntimeServerPrerender(
733732

734733
// The cacheSignal helps us track whether caches are still filling or we are ready
735734
// to cut the render off.
736-
const cacheSignal = new CacheSignal()
735+
const cacheSignal = new CacheSignal('prerender')
737736

738737
const initialServerPrerenderStore: PrerenderStoreModernRuntime = {
739738
type: 'prerender-runtime',
@@ -2184,12 +2183,12 @@ async function renderToStream(
21842183

21852184
// This render might end up being used as a prospective render (if there's cache misses),
21862185
// so we need to set it up for filling caches.
2187-
const cacheSignal = new CacheSignal()
2186+
const cacheSignal = new CacheSignal('render')
21882187

21892188
// If we encounter async modules that delay rendering, we'll also need to restart.
21902189
// TODO(restart-on-cache-miss): technically, we only need to wait for pending *server* modules here,
21912190
// but `trackPendingModules` doesn't distinguish between client and server.
2192-
trackPendingModulesInRender(cacheSignal)
2191+
trackPendingModules(cacheSignal)
21932192

21942193
const prerenderResumeDataCache = createPrerenderResumeDataCache()
21952194

@@ -2265,11 +2264,7 @@ async function renderToStream(
22652264
// Ideally we'd only wait for caches that are needed in the static stage.
22662265
// This will be optimized in the future by not allowing runtime/dynamic APIs to resolve.
22672266

2268-
// During a render, React pings pending tasks using `setImmediate`,
2269-
// and only waiting for a single `cacheReady` can make us stop filling caches too soon.
2270-
// To avoid this, we await `cacheReady` repeatedly with an extra delay to let React try render new content
2271-
// (and potentially discover more caches).
2272-
await cacheSignal.cacheReadyInRender()
2267+
await cacheSignal.cacheReady()
22732268
initialRenderReactController.abort()
22742269

22752270
//===============================================
@@ -2805,7 +2800,7 @@ async function spawnDynamicValidationInDev(
28052800

28062801
// The cacheSignal helps us track whether caches are still filling or we are
28072802
// ready to cut the render off.
2808-
const cacheSignal = new CacheSignal()
2803+
const cacheSignal = new CacheSignal('prerender')
28092804

28102805
const captureOwnerStackClient = React.captureOwnerStack
28112806
const captureOwnerStackServer = ComponentMod.captureOwnerStack
@@ -3545,7 +3540,7 @@ async function prerenderToStream(
35453540

35463541
// The cacheSignal helps us track whether caches are still filling or we are ready
35473542
// to cut the render off.
3548-
const cacheSignal = new CacheSignal()
3543+
const cacheSignal = new CacheSignal('prerender')
35493544

35503545
let resumeDataCache: RenderResumeDataCache | PrerenderResumeDataCache
35513546
let renderResumeDataCache: RenderResumeDataCache | null = null

packages/next/src/server/app-render/cache-signal.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import { waitAtLeastOneReactRenderTask } from '../../lib/scheduler'
99
import { InvariantError } from '../../shared/lib/invariant-error'
1010

11+
type CacheSignalMode = 'prerender' | 'render'
12+
1113
export class CacheSignal {
1214
private count = 0
1315
private earlyListeners: Array<() => void> = []
@@ -17,7 +19,7 @@ export class CacheSignal {
1719

1820
private subscribedSignals: Set<CacheSignal> | null = null
1921

20-
constructor() {
22+
constructor(private mode: CacheSignalMode) {
2123
if (process.env.NEXT_RUNTIME === 'edge') {
2224
// we rely on `process.nextTick`, which is not supported in edge
2325
throw new InvariantError(
@@ -71,7 +73,23 @@ export class CacheSignal {
7173
* if there are no inflight cache reads then we wait at least one task to allow initial
7274
* cache reads to be initiated.
7375
*/
74-
cacheReady() {
76+
async cacheReady(): Promise<void> {
77+
if (this.mode === 'prerender') {
78+
return await this.cacheReadyImpl()
79+
} else {
80+
// During a render, React pings pending tasks (that are waiting for something async to resolve) using `setImmediate`.
81+
// This is unlike a prerender, where they are pinged in a microtask.
82+
// This means that, if we're warming caches via a render (not a prerender),
83+
// we need to give React more time to continue rendering after a cache has resolved
84+
// in order to make sure we've discovered all the caches needed for the current render.
85+
do {
86+
await this.cacheReadyImpl()
87+
await waitAtLeastOneReactRenderTask()
88+
} while (this.hasPendingReads())
89+
}
90+
}
91+
92+
private cacheReadyImpl() {
7593
return new Promise<void>((resolve) => {
7694
this.listeners.push(resolve)
7795
if (this.count === 0) {
@@ -80,23 +98,6 @@ export class CacheSignal {
8098
})
8199
}
82100

83-
/**
84-
* Like `cacheReady`, but for use when rendering (not prerendering).
85-
* React schedules work differently between these two, which affects the timing
86-
* of waiting for all caches to be discovered.
87-
**/
88-
async cacheReadyInRender() {
89-
// During a render, React pings pending tasks (that are waiting for something async to resolve) using `setImmediate`.
90-
// This is unlike a prerender, where they are pinged in a microtask.
91-
// This means that, if we're warming caches via a render (not a prerender),
92-
// we need to give React more time to continue rendering after a cache has resolved
93-
// in order to make sure we've discovered all the caches needed for the current render.
94-
do {
95-
await this.cacheReady()
96-
await waitAtLeastOneReactRenderTask()
97-
} while (this.hasPendingReads())
98-
}
99-
100101
beginRead() {
101102
this.count++
102103

packages/next/src/server/app-render/module-loading/track-module-loading.external.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ import {
66
trackPendingChunkLoad,
77
trackPendingImport,
88
trackPendingModules,
9-
trackPendingModulesInRender,
109
} from './track-module-loading.instance' with { 'turbopack-transition': 'next-shared' }
1110

12-
export {
13-
trackPendingChunkLoad,
14-
trackPendingImport,
15-
trackPendingModules,
16-
trackPendingModulesInRender,
17-
}
11+
export { trackPendingChunkLoad, trackPendingImport, trackPendingModules }

packages/next/src/server/app-render/module-loading/track-module-loading.instance.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { isThenable } from '../../../shared/lib/is-thenable'
88
let _moduleLoadingSignal: CacheSignal | null
99
function getModuleLoadingSignal() {
1010
if (!_moduleLoadingSignal) {
11-
_moduleLoadingSignal = new CacheSignal()
11+
// The mode doesn't really matter here, because we never wait for `cacheReady()` on this signal,
12+
// only other signals that subscribe to it via `trackPendingModules`.
13+
_moduleLoadingSignal = new CacheSignal('prerender')
1214
}
1315
return _moduleLoadingSignal
1416
}
@@ -52,21 +54,3 @@ export function trackPendingModules(cacheSignal: CacheSignal): void {
5254
// we can unsubscribe it.
5355
cacheSignal.cacheReady().then(unsubscribe)
5456
}
55-
56-
/**
57-
* Like `trackPendingModules`, but for use in a render, not a prerender.
58-
* See `CacheSignal#cacheReadyInRender` for an explanation.
59-
*/
60-
export function trackPendingModulesInRender(cacheSignal: CacheSignal): void {
61-
const moduleLoadingSignal = getModuleLoadingSignal()
62-
63-
// We can't just use `cacheSignal.trackRead(moduleLoadingSignal.cacheReady())`,
64-
// because we might start and finish multiple batches of module loads while waiting for caches,
65-
// and `moduleLoadingSignal.cacheReady()` would resolve after the first batch.
66-
// Instead, we'll keep notifying `cacheSignal` of each import/chunk-load.
67-
const unsubscribe = moduleLoadingSignal.subscribeToReads(cacheSignal)
68-
69-
// Later, when `cacheSignal` is no longer waiting for any caches (or imports that we've notified it of),
70-
// we can unsubscribe it.
71-
cacheSignal.cacheReadyInRender().then(unsubscribe)
72-
}

packages/next/src/server/route-modules/app-route/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ export class AppRouteRouteModule extends RouteModule<
376376
*/
377377
const prospectiveController = new AbortController()
378378
let prospectiveRenderIsDynamic = false
379-
const cacheSignal = new CacheSignal()
379+
const cacheSignal = new CacheSignal('prerender')
380380
let dynamicTracking = createDynamicTrackingState(undefined)
381381

382382
// TODO: Route handlers are never resumed, so it's counter-intuitive

0 commit comments

Comments
 (0)