Skip to content

Commit 00f030d

Browse files
committed
[Flight] model halted references explicitly
using infinitely suspending promises isn't right because this will parse as a promise which is only appropriate if the value we're halting at is a promise. Instead we need to have a special marker type that says this reference will never resolve. Additionally flight client needs to not error any halted references when the stream closes because they will otherwise appear as an error
1 parent 7954db9 commit 00f030d

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
enableRefAsProp,
4646
enableFlightReadableStream,
4747
enableOwnerStacks,
48+
enableHalt,
4849
} from 'shared/ReactFeatureFlags';
4950

5051
import {
@@ -860,6 +861,27 @@ function getChunk(response: Response, id: number): SomeChunk<any> {
860861
return chunk;
861862
}
862863

864+
/**
865+
* Fork of waitForReference that doesn't ever reslve
866+
*/
867+
function waitForever() {
868+
let handler: InitializationHandler;
869+
if (initializingHandler) {
870+
handler = initializingHandler;
871+
handler.deps++;
872+
} else {
873+
handler = initializingHandler = {
874+
parent: null,
875+
chunk: null,
876+
value: null,
877+
deps: 1,
878+
errored: false,
879+
};
880+
}
881+
882+
return null;
883+
}
884+
863885
function waitForReference<T>(
864886
referencedChunk: SomeChunk<T>,
865887
parentObject: Object,
@@ -1227,6 +1249,14 @@ function parseModelString(
12271249
}
12281250
return readTemporaryReference(temporaryReferences, reference);
12291251
}
1252+
case '&': {
1253+
if (enableHalt) {
1254+
return waitForever();
1255+
}
1256+
// It'd be an error if this ever happened when this flag is off
1257+
// but that shoudl be impossibe
1258+
// fallthrough
1259+
}
12301260
case 'Q': {
12311261
// Map
12321262
const ref = value.slice(2);

packages/react-server/src/ReactFlightServer.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ function serializeThenable(
615615
request.abortableTasks.delete(newTask);
616616
newTask.status = ABORTED;
617617
if (enableHalt && request.fatalError === haltSymbol) {
618-
emitModelChunk(request, newTask.id, reusableInfinitePromiseModel);
618+
emitModelChunk(request, newTask.id, reusableHaltedReferenceModel);
619619
} else {
620620
const errorId: number = (request.fatalError: any);
621621
const model = stringify(serializeByValueID(errorId));
@@ -1818,7 +1818,6 @@ function serializeLazyID(id: number): string {
18181818
function serializeInfinitePromise(): string {
18191819
return '$@';
18201820
}
1821-
const reusableInfinitePromiseModel = stringify(serializeInfinitePromise());
18221821

18231822
function serializePromiseID(id: number): string {
18241823
return '$@' + id.toString(16);
@@ -1836,6 +1835,11 @@ function serializeLimitedObject(): string {
18361835
return '$Y';
18371836
}
18381837

1838+
function serializeHaltedReference(): string {
1839+
return '$&';
1840+
}
1841+
const reusableHaltedReferenceModel = '"$&"';
1842+
18391843
function serializeNumber(number: number): string | number {
18401844
if (Number.isFinite(number)) {
18411845
if (number === 0 && 1 / number === -Infinity) {
@@ -2177,7 +2181,7 @@ function renderModel(
21772181
if (request.status === ABORTING) {
21782182
task.status = ABORTED;
21792183
if (enableHalt && request.fatalError === haltSymbol) {
2180-
return serializeInfinitePromise();
2184+
return serializeHaltedReference();
21812185
}
21822186
const errorId: number = (request.fatalError: any);
21832187
if (wasReactNode) {
@@ -2233,7 +2237,7 @@ function renderModel(
22332237
if (request.status === ABORTING) {
22342238
task.status = ABORTED;
22352239
if (enableHalt && request.fatalError === haltSymbol) {
2236-
return serializeInfinitePromise();
2240+
return serializeHaltedReference();
22372241
}
22382242
const errorId: number = (request.fatalError: any);
22392243
if (wasReactNode) {
@@ -3725,7 +3729,7 @@ function retryTask(request: Request, task: Task): void {
37253729
request.abortableTasks.delete(task);
37263730
task.status = ABORTED;
37273731
if (enableHalt && request.fatalError === haltSymbol) {
3728-
emitModelChunk(request, task.id, reusableInfinitePromiseModel);
3732+
emitModelChunk(request, task.id, reusableHaltedReferenceModel);
37293733
} else {
37303734
const errorId: number = (request.fatalError: any);
37313735
const model = stringify(serializeByValueID(errorId));
@@ -3753,7 +3757,7 @@ function retryTask(request: Request, task: Task): void {
37533757
request.abortableTasks.delete(task);
37543758
task.status = ABORTED;
37553759
if (enableHalt && request.fatalError === haltSymbol) {
3756-
emitModelChunk(request, task.id, reusableInfinitePromiseModel);
3760+
emitModelChunk(request, task.id, reusableHaltedReferenceModel);
37573761
} else {
37583762
const errorId: number = (request.fatalError: any);
37593763
const model = stringify(serializeByValueID(errorId));
@@ -3842,7 +3846,7 @@ function haltTask(task: Task, request: Request): void {
38423846
return;
38433847
}
38443848
task.status = ABORTED;
3845-
emitModelChunk(request, task.id, reusableInfinitePromiseModel);
3849+
emitModelChunk(request, task.id, reusableHaltedReferenceModel);
38463850
}
38473851

38483852
function flushCompletedChunks(

0 commit comments

Comments
 (0)