@samitouri / QOS-React / commits / fc5ef50da8

[Flight] Start initial work immediately (#30961)

In a past update we made render and prerender have different work scheduling behavior because these methods are meant to be used in differeent environments with different performance tradeoffs in mind. For instance to prioritize streaming we want to allow as much IO to complete before triggering a round of work because we want to flush as few intermediate UI states. With Prerendering there will never be any intermediate UI states so we can more aggressively render tasks as they complete. One thing we've found is that even during render we should ideally kick off work immediately. This update normalizes the intitial work for render and prerender to start in a microtask. Choosing microtask over sync is somewhat arbitrary but there really isn't a reason to make them different between render/prerender so for now we'll unify them and keep it as a microtask for now. This change also updates pinging behavior. If the request is still in the initial task that spawned it then pings will schedule on the microtask queue. This allows immediately available async APIs to resolve right away. The concern with doing this for normal pings is that it might crowd out IO events but since this is the initial task there would be IO to already be scheduled.

Josh Story committed Sep 14, 2024 at 09:26 UTC fc5ef50da8e975a569622d477f1fed54cb8b193d
1 file changed +24 -25
packages/react-server/src/ReactFlightServer.js
+24 -25
@@ -352,8 +352,17 @@ type Task = {
352
353 interface Reference {}
354
355 +const OPENING = 10;
356 +const OPEN = 11;
357 +const ABORTING = 12;
358 +const CLOSING = 13;
359 +const CLOSED = 14;
360 +
361 +const RENDER = 20;
362 +const PRERENDER = 21;
363 +
364 export type Request = {
356 - status: 10 | 11 | 12 | 13,
365 + status: 10 | 11 | 12 | 13 | 14,
366 type: 20 | 21,
367 flushScheduled: boolean,
368 fatalError: mixed,
@@ -426,14 +435,6 @@ function defaultPostponeHandler(reason: string) {
435 // Noop
436 }
437
429 -const OPEN = 10;
430 -const ABORTING = 11;
431 -const CLOSING = 12;
432 -const CLOSED = 13;
433 -
434 -const RENDER = 20;
435 -const PRERENDER = 21;
436 -
438 function RequestInstance(
439 this: $FlowFixMe,
440 type: 20 | 21,
@@ -472,7 +473,7 @@ function RequestInstance(
473 }
474 const hints = createHints();
475 this.type = type;
475 - this.status = OPEN;
476 + this.status = OPENING;
477 this.flushScheduled = false;
478 this.fatalError = null;
479 this.destination = null;
@@ -1794,7 +1795,7 @@ function pingTask(request: Request, task: Task): void {
1795 pingedTasks.push(task);
1796 if (pingedTasks.length === 1) {
1797 request.flushScheduled = request.destination !== null;
1797 - if (request.type === PRERENDER) {
1798 + if (request.type === PRERENDER || request.status === OPENING) {
1799 scheduleMicrotask(() => performWork(request));
1800 } else {
1801 scheduleWork(() => performWork(request));
@@ -4062,21 +4063,18 @@ function flushCompletedChunks(
4063
4064 export function startWork(request: Request): void {
4065 request.flushScheduled = request.destination !== null;
4065 - if (request.type === PRERENDER) {
4066 - if (supportsRequestStorage) {
4067 - scheduleMicrotask(() => {
4068 - requestStorage.run(request, performWork, request);
4069 - });
4070 - } else {
4071 - scheduleMicrotask(() => performWork(request));
4072 - }
4066 + if (supportsRequestStorage) {
4067 + scheduleMicrotask(() => {
4068 + requestStorage.run(request, performWork, request);
4069 + });
4070 } else {
4074 - if (supportsRequestStorage) {
4075 - scheduleWork(() => requestStorage.run(request, performWork, request));
4076 - } else {
4077 - scheduleWork(() => performWork(request));
4078 - }
4071 + scheduleMicrotask(() => performWork(request));
4072 }
4073 + scheduleWork(() => {
4074 + if (request.status === OPENING) {
4075 + request.status = OPEN;
4076 + }
4077 + });
4078 }
4079
4080 function enqueueFlush(request: Request): void {
@@ -4129,7 +4127,8 @@ export function stopFlowing(request: Request): void {
4127
4128 export function abort(request: Request, reason: mixed): void {
4129 try {
4132 - if (request.status === OPEN) {
4130 + // We define any status below OPEN as OPEN equivalent
4131 + if (request.status <= OPEN) {
4132 request.status = ABORTING;
4133 }
4134 const abortableTasks = request.abortableTasks;