@samitouri / QOS-React-1 / commits / 953cb02f6d

[Fizz] Split createRequest into createRequest, createPrerenderRequest and resumeRequest (#27342)

Just moving some internal code around again. I originally encoded what type of work using startRender vs startPrerender. I had intended to do more forking of the work loop but we've decided not to go with that strategy. It also turns out that forking when we start working is actually too late because of a subtle thing where you can call abort before work begins. Therefore it's important that starting the work comes later.

Sebastian Markbåge committed Sep 7, 2023 at 00:18 UTC 953cb02f6de2a9f3eb52456b263e11f839584bc6
18 files changed +150 -59
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+6 -7
@@ -16,7 +16,8 @@ import ReactVersion from 'shared/ReactVersion';
16
17 import {
18 createRequest,
19 - startRender,
19 + resumeRequest,
20 + startWork,
21 startFlowing,
22 abort,
23 } from 'react-server/src/ReactFizzServer';
@@ -129,7 +130,7 @@ function renderToReadableStream(
130 signal.addEventListener('abort', listener);
131 }
132 }
132 - startRender(request);
133 + startWork(request);
134 });
135 }
136
@@ -171,16 +172,14 @@ function resume(
172 allReady.catch(() => {});
173 reject(error);
174 }
174 - const request = createRequest(
175 + const request = resumeRequest(
176 children,
176 - postponedState.resumableState,
177 + postponedState,
178 createRenderState(
179 postponedState.resumableState,
180 options ? options.nonce : undefined,
181 undefined, // importMap
182 ),
182 - postponedState.rootFormatContext,
183 - postponedState.progressiveChunkSize,
183 options ? options.onError : undefined,
184 onAllReady,
185 onShellReady,
@@ -200,7 +199,7 @@ function resume(
199 signal.addEventListener('abort', listener);
200 }
201 }
203 - startRender(request);
202 + startWork(request);
203 });
204 }
205
packages/react-dom/src/server/ReactDOMFizzServerBun.js
+2 -2
@@ -15,7 +15,7 @@ import ReactVersion from 'shared/ReactVersion';
15
16 import {
17 createRequest,
18 - startRender,
18 + startWork,
19 startFlowing,
20 abort,
21 } from 'react-server/src/ReactFizzServer';
@@ -121,7 +121,7 @@ function renderToReadableStream(
121 signal.addEventListener('abort', listener);
122 }
123 }
124 - startRender(request);
124 + startWork(request);
125 });
126 }
127
packages/react-dom/src/server/ReactDOMFizzServerEdge.js
+6 -7
@@ -16,7 +16,8 @@ import ReactVersion from 'shared/ReactVersion';
16
17 import {
18 createRequest,
19 - startRender,
19 + resumeRequest,
20 + startWork,
21 startFlowing,
22 abort,
23 } from 'react-server/src/ReactFizzServer';
@@ -129,7 +130,7 @@ function renderToReadableStream(
130 signal.addEventListener('abort', listener);
131 }
132 }
132 - startRender(request);
133 + startWork(request);
134 });
135 }
136
@@ -171,16 +172,14 @@ function resume(
172 allReady.catch(() => {});
173 reject(error);
174 }
174 - const request = createRequest(
175 + const request = resumeRequest(
176 children,
176 - postponedState.resumableState,
177 + postponedState,
178 createRenderState(
179 postponedState.resumableState,
180 options ? options.nonce : undefined,
181 undefined, // importMap
182 ),
182 - postponedState.rootFormatContext,
183 - postponedState.progressiveChunkSize,
183 options ? options.onError : undefined,
184 onAllReady,
185 onShellReady,
@@ -200,7 +199,7 @@ function resume(
199 signal.addEventListener('abort', listener);
200 }
201 }
203 - startRender(request);
202 + startWork(request);
203 });
204 }
205
packages/react-dom/src/server/ReactDOMFizzServerNode.js
+6 -7
@@ -18,7 +18,8 @@ import ReactVersion from 'shared/ReactVersion';
18
19 import {
20 createRequest,
21 - startRender,
21 + resumeRequest,
22 + startWork,
23 startFlowing,
24 abort,
25 } from 'react-server/src/ReactFizzServer';
@@ -105,7 +106,7 @@ function renderToPipeableStream(
106 ): PipeableStream {
107 const request = createRequestImpl(children, options);
108 let hasStartedFlowing = false;
108 - startRender(request);
109 + startWork(request);
110 return {
111 pipe<T: Writable>(destination: T): T {
112 if (hasStartedFlowing) {
@@ -140,16 +141,14 @@ function resumeRequestImpl(
141 postponedState: PostponedState,
142 options: void | ResumeOptions,
143 ) {
143 - return createRequest(
144 + return resumeRequest(
145 children,
145 - postponedState.resumableState,
146 + postponedState,
147 createRenderState(
148 postponedState.resumableState,
149 options ? options.nonce : undefined,
150 undefined, // importMap
151 ),
151 - postponedState.rootFormatContext,
152 - postponedState.progressiveChunkSize,
152 options ? options.onError : undefined,
153 options ? options.onAllReady : undefined,
154 options ? options.onShellReady : undefined,
@@ -166,7 +165,7 @@ function resumeToPipeableStream(
165 ): PipeableStream {
166 const request = resumeRequestImpl(children, postponedState, options);
167 let hasStartedFlowing = false;
169 - startRender(request);
168 + startWork(request);
169 return {
170 pipe<T: Writable>(destination: T): T {
171 if (hasStartedFlowing) {
packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+4 -4
@@ -15,8 +15,8 @@ import type {ImportMap} from '../shared/ReactDOMTypes';
15 import ReactVersion from 'shared/ReactVersion';
16
17 import {
18 - createRequest,
19 - startPrerender,
18 + createPrerenderRequest,
19 + startWork,
20 startFlowing,
21 abort,
22 getPostponedState,
@@ -80,7 +80,7 @@ function prerender(
80 options ? options.bootstrapModules : undefined,
81 options ? options.unstable_externalRuntimeSrc : undefined,
82 );
83 - const request = createRequest(
83 + const request = createPrerenderRequest(
84 children,
85 resources,
86 createRenderState(
@@ -109,7 +109,7 @@ function prerender(
109 signal.addEventListener('abort', listener);
110 }
111 }
112 - startPrerender(request);
112 + startWork(request);
113 });
114 }
115
packages/react-dom/src/server/ReactDOMFizzStaticEdge.js
+4 -4
@@ -15,8 +15,8 @@ import type {ImportMap} from '../shared/ReactDOMTypes';
15 import ReactVersion from 'shared/ReactVersion';
16
17 import {
18 - createRequest,
19 - startPrerender,
18 + createPrerenderRequest,
19 + startWork,
20 startFlowing,
21 abort,
22 getPostponedState,
@@ -80,7 +80,7 @@ function prerender(
80 options ? options.bootstrapModules : undefined,
81 options ? options.unstable_externalRuntimeSrc : undefined,
82 );
83 - const request = createRequest(
83 + const request = createPrerenderRequest(
84 children,
85 resources,
86 createRenderState(
@@ -109,7 +109,7 @@ function prerender(
109 signal.addEventListener('abort', listener);
110 }
111 }
112 - startPrerender(request);
112 + startWork(request);
113 });
114 }
115
packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+4 -4
@@ -17,8 +17,8 @@ import {Writable, Readable} from 'stream';
17 import ReactVersion from 'shared/ReactVersion';
18
19 import {
20 - createRequest,
21 - startPrerender,
20 + createPrerenderRequest,
21 + startWork,
22 startFlowing,
23 abort,
24 getPostponedState,
@@ -94,7 +94,7 @@ function prerenderToNodeStream(
94 options ? options.bootstrapModules : undefined,
95 options ? options.unstable_externalRuntimeSrc : undefined,
96 );
97 - const request = createRequest(
97 + const request = createPrerenderRequest(
98 children,
99 resumableState,
100 createRenderState(
@@ -123,7 +123,7 @@ function prerenderToNodeStream(
123 signal.addEventListener('abort', listener);
124 }
125 }
126 - startPrerender(request);
126 + startWork(request);
127 });
128 }
129
packages/react-dom/src/server/ReactDOMLegacyServerImpl.js
+2 -2
@@ -13,7 +13,7 @@ import type {ReactNodeList} from 'shared/ReactTypes';
13
14 import {
15 createRequest,
16 - startRender,
16 + startWork,
17 startFlowing,
18 abort,
19 } from 'react-server/src/ReactFizzServer';
@@ -81,7 +81,7 @@ function renderToStringImpl(
81 undefined,
82 undefined,
83 );
84 - startRender(request);
84 + startWork(request);
85 // If anything suspended and is still pending, we'll abort it before writing.
86 // That way we write only client-rendered boundaries from the start.
87 abort(request, abortReason);
packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js
+2 -2
@@ -13,7 +13,7 @@ import type {Request} from 'react-server/src/ReactFizzServer';
13
14 import {
15 createRequest,
16 - startRender,
16 + startWork,
17 startFlowing,
18 abort,
19 } from 'react-server/src/ReactFizzServer';
@@ -92,7 +92,7 @@ function renderToNodeStreamImpl(
92 undefined,
93 );
94 destination.request = request;
95 - startRender(request);
95 + startWork(request);
96 return destination;
97 }
98
packages/react-noop-renderer/src/ReactNoopFlightServer.js
+1 -1
@@ -84,7 +84,7 @@ function render(model: ReactClientValue, options?: Options): Destination {
84 options ? options.context : undefined,
85 options ? options.identifierPrefix : undefined,
86 );
87 - ReactNoopFlightServer.startRender(request);
87 + ReactNoopFlightServer.startWork(request);
88 ReactNoopFlightServer.startFlowing(request, destination);
89 return destination;
90 }
packages/react-noop-renderer/src/ReactNoopServer.js
+1 -1
@@ -304,7 +304,7 @@ function render(children: React$Element<any>, options?: Options): Destination {
304 options ? options.onAllReady : undefined,
305 options ? options.onShellReady : undefined,
306 );
307 - ReactNoopServer.startRender(request);
307 + ReactNoopServer.startWork(request);
308 ReactNoopServer.startFlowing(request, destination);
309 return destination;
310 }
packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js
+2 -2
@@ -20,7 +20,7 @@ import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
20
21 import {
22 createRequest,
23 - startRender,
23 + startWork,
24 startFlowing,
25 abort,
26 } from 'react-server/src/ReactFlightServer';
@@ -73,7 +73,7 @@ function renderToPipeableStream(
73 options ? options.onPostpone : undefined,
74 );
75 let hasStartedFlowing = false;
76 - startRender(request);
76 + startWork(request);
77 return {
78 pipe<T: Writable>(destination: T): T {
79 if (hasStartedFlowing) {
packages/react-server-dom-fb/src/ReactDOMServerFB.js
+2 -2
@@ -16,7 +16,7 @@ import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/Reac
16
17 import {
18 createRequest,
19 - startRender,
19 + startWork,
20 performWork,
21 startFlowing,
22 abort,
@@ -68,7 +68,7 @@ function renderToStream(children: ReactNodeList, options: Options): Stream {
68 undefined,
69 undefined,
70 );
71 - startRender(request);
71 + startWork(request);
72 if (destination.fatal) {
73 throw destination.error;
74 }
packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js
+2 -2
@@ -14,7 +14,7 @@ import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
14
15 import {
16 createRequest,
17 - startRender,
17 + startWork,
18 startFlowing,
19 abort,
20 } from 'react-server/src/ReactFlightServer';
@@ -70,7 +70,7 @@ function renderToReadableStream(
70 {
71 type: 'bytes',
72 start: (controller): ?Promise<void> => {
73 - startRender(request);
73 + startWork(request);
74 },
75 pull: (controller): ?Promise<void> => {
76 startFlowing(request, controller);
packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js
+2 -2
@@ -14,7 +14,7 @@ import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
14
15 import {
16 createRequest,
17 - startRender,
17 + startWork,
18 startFlowing,
19 abort,
20 } from 'react-server/src/ReactFlightServer';
@@ -70,7 +70,7 @@ function renderToReadableStream(
70 {
71 type: 'bytes',
72 start: (controller): ?Promise<void> => {
73 - startRender(request);
73 + startWork(request);
74 },
75 pull: (controller): ?Promise<void> => {
76 startFlowing(request, controller);
packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js
+2 -2
@@ -20,7 +20,7 @@ import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
20
21 import {
22 createRequest,
23 - startRender,
23 + startWork,
24 startFlowing,
25 abort,
26 } from 'react-server/src/ReactFlightServer';
@@ -74,7 +74,7 @@ function renderToPipeableStream(
74 options ? options.onPostpone : undefined,
75 );
76 let hasStartedFlowing = false;
77 - startRender(request);
77 + startWork(request);
78 return {
79 pipe<T: Writable>(destination: T): T {
80 if (hasStartedFlowing) {
packages/react-server/src/ReactFizzServer.js
+101 -7
@@ -396,6 +396,106 @@ export function createRequest(
396 return request;
397 }
398
399 +export function createPrerenderRequest(
400 + children: ReactNodeList,
401 + resumableState: ResumableState,
402 + renderState: RenderState,
403 + rootFormatContext: FormatContext,
404 + progressiveChunkSize: void | number,
405 + onError: void | ((error: mixed) => ?string),
406 + onAllReady: void | (() => void),
407 + onShellReady: void | (() => void),
408 + onShellError: void | ((error: mixed) => void),
409 + onFatalError: void | ((error: mixed) => void),
410 + onPostpone: void | ((reason: string) => void),
411 +): Request {
412 + const request = createRequest(
413 + children,
414 + resumableState,
415 + renderState,
416 + rootFormatContext,
417 + progressiveChunkSize,
418 + onError,
419 + onAllReady,
420 + onShellReady,
421 + onShellError,
422 + onFatalError,
423 + onPostpone,
424 + );
425 + // Start tracking postponed holes during this render.
426 + request.trackedPostpones = {workingMap: new Map(), root: []};
427 + return request;
428 +}
429 +
430 +export function resumeRequest(
431 + children: ReactNodeList,
432 + postponedState: PostponedState,
433 + renderState: RenderState,
434 + onError: void | ((error: mixed) => ?string),
435 + onAllReady: void | (() => void),
436 + onShellReady: void | (() => void),
437 + onShellError: void | ((error: mixed) => void),
438 + onFatalError: void | ((error: mixed) => void),
439 + onPostpone: void | ((reason: string) => void),
440 +): Request {
441 + prepareHostDispatcher();
442 + const pingedTasks: Array<Task> = [];
443 + const abortSet: Set<Task> = new Set();
444 + const request: Request = {
445 + destination: null,
446 + flushScheduled: false,
447 + resumableState: postponedState.resumableState,
448 + renderState,
449 + rootFormatContext: postponedState.rootFormatContext,
450 + progressiveChunkSize: postponedState.progressiveChunkSize,
451 + status: OPEN,
452 + fatalError: null,
453 + nextSegmentId: 0,
454 + allPendingTasks: 0,
455 + pendingRootTasks: 0,
456 + completedRootSegment: null,
457 + abortableTasks: abortSet,
458 + pingedTasks: pingedTasks,
459 + clientRenderedBoundaries: ([]: Array<SuspenseBoundary>),
460 + completedBoundaries: ([]: Array<SuspenseBoundary>),
461 + partialBoundaries: ([]: Array<SuspenseBoundary>),
462 + trackedPostpones: null,
463 + onError: onError === undefined ? defaultErrorHandler : onError,
464 + onPostpone: onPostpone === undefined ? noop : onPostpone,
465 + onAllReady: onAllReady === undefined ? noop : onAllReady,
466 + onShellReady: onShellReady === undefined ? noop : onShellReady,
467 + onShellError: onShellError === undefined ? noop : onShellError,
468 + onFatalError: onFatalError === undefined ? noop : onFatalError,
469 + };
470 + // This segment represents the root fallback.
471 + const rootSegment = createPendingSegment(
472 + request,
473 + 0,
474 + null,
475 + postponedState.rootFormatContext,
476 + // Root segments are never embedded in Text on either edge
477 + false,
478 + false,
479 + );
480 + // There is no parent so conceptually, we're unblocked to flush this segment.
481 + rootSegment.parentFlushed = true;
482 + const rootTask = createTask(
483 + request,
484 + null,
485 + children,
486 + null,
487 + rootSegment,
488 + abortSet,
489 + null,
490 + postponedState.rootFormatContext,
491 + emptyContextObject,
492 + rootContextSnapshot,
493 + emptyTreeContext,
494 + );
495 + pingedTasks.push(rootTask);
496 + return request;
497 +}
498 +
499 let currentRequest: null | Request = null;
500
501 export function resolveRequest(): null | Request {
@@ -2751,7 +2851,7 @@ function flushCompletedQueues(
2851 }
2852 }
2853
2754 -export function startRender(request: Request): void {
2854 +export function startWork(request: Request): void {
2855 request.flushScheduled = request.destination !== null;
2856 if (supportsRequestStorage) {
2857 scheduleWork(() => requestStorage.run(request, performWork, request));
@@ -2760,12 +2860,6 @@ export function startRender(request: Request): void {
2860 }
2861 }
2862
2763 -export function startPrerender(request: Request): void {
2764 - // Start tracking postponed holes during this render.
2765 - request.trackedPostpones = {workingMap: new Map(), root: []};
2766 - startRender(request);
2767 -}
2768 -
2863 function enqueueFlush(request: Request): void {
2864 if (
2865 request.flushScheduled === false &&
packages/react-server/src/ReactFlightServer.js
+1 -1
@@ -1519,7 +1519,7 @@ function flushCompletedChunks(
1519 }
1520 }
1521
1522 -export function startRender(request: Request): void {
1522 +export function startWork(request: Request): void {
1523 request.flushScheduled = request.destination !== null;
1524 if (supportsRequestStorage) {
1525 scheduleWork(() => requestStorage.run(request, performWork, request));