@@ -649,9 +649,13 @@ function serializeThenable(
649
649
// We can no longer accept any resolved values
650
650
request . abortableTasks . delete ( newTask ) ;
651
651
newTask . status = ABORTED ;
652
- const errorId : number = ( request . fatalError : any ) ;
653
- const model = stringify ( serializeByValueID ( errorId ) ) ;
654
- emitModelChunk ( request , newTask . id , model ) ;
652
+ if ( enableHalt && request . type === PRERENDER ) {
653
+ request . pendingChunks -- ;
654
+ } else {
655
+ const errorId : number = ( request . fatalError : any ) ;
656
+ const model = stringify ( serializeByValueID ( errorId ) ) ;
657
+ emitModelChunk ( request , newTask . id , model ) ;
658
+ }
655
659
return newTask . id ;
656
660
}
657
661
if ( typeof thenable . status === 'string' ) {
@@ -1633,6 +1637,33 @@ function outlineTask(request: Request, task: Task): ReactJSONValue {
1633
1637
return serializeLazyID(newTask.id);
1634
1638
}
1635
1639
1640
+ function outlineHaltedTask (
1641
+ request : Request ,
1642
+ task : Task ,
1643
+ allowLazy : boolean ,
1644
+ ) : ReactJSONValue {
1645
+ const newTask = createTask (
1646
+ request ,
1647
+ task . model , // the currently rendering element
1648
+ task . keyPath , // unlike outlineModel this one carries along context
1649
+ task . implicitSlot ,
1650
+ request . abortableTasks ,
1651
+ __DEV__ ? task . debugOwner : null ,
1652
+ __DEV__ && enableOwnerStacks ? task . debugStack : null ,
1653
+ __DEV__ && enableOwnerStacks ? task . debugTask : null ,
1654
+ ) ;
1655
+
1656
+ // We don't actually try the new task because it is being halted.
1657
+ haltTask ( newTask , request ) ;
1658
+ if ( allowLazy ) {
1659
+ // We're halting in a position that can handle a lazy reference
1660
+ return serializeLazyID ( newTask . id ) ;
1661
+ } else {
1662
+ // We're halting in a position that needs a value reference
1663
+ return serializeByValueID ( newTask . id ) ;
1664
+ }
1665
+ }
1666
+
1636
1667
function renderElement (
1637
1668
request : Request ,
1638
1669
task : Task ,
@@ -2278,6 +2309,20 @@ function renderModel(
2278
2309
( ( model : any ) . $$typeof === REACT_ELEMENT_TYPE ||
2279
2310
( model : any ) . $$typeof === REACT_LAZY_TYPE ) ;
2280
2311
2312
+ if ( request . status === ABORTING ) {
2313
+ task . status = ABORTED ;
2314
+ if ( enableHalt && request . type === PRERENDER ) {
2315
+ // This will create a new task and refer to it in this slot
2316
+ // the new task won't be retried because we are aborting
2317
+ return outlineHaltedTask ( request , task , wasReactNode ) ;
2318
+ }
2319
+ const errorId = ( request . fatalError : any ) ;
2320
+ if ( wasReactNode ) {
2321
+ return serializeLazyID ( errorId ) ;
2322
+ }
2323
+ return serializeByValueID ( errorId ) ;
2324
+ }
2325
+
2281
2326
const x =
2282
2327
thrownValue === SuspenseException
2283
2328
? // This is a special type of exception used for Suspense. For historical
@@ -2291,14 +2336,6 @@ function renderModel(
2291
2336
if ( typeof x === 'object' && x !== null ) {
2292
2337
// $FlowFixMe[method-unbinding]
2293
2338
if ( typeof x . then === 'function' ) {
2294
- if ( request . status === ABORTING ) {
2295
- task . status = ABORTED ;
2296
- const errorId : number = ( request . fatalError : any ) ;
2297
- if ( wasReactNode ) {
2298
- return serializeLazyID ( errorId ) ;
2299
- }
2300
- return serializeByValueID ( errorId ) ;
2301
- }
2302
2339
// Something suspended, we'll need to create a new task and resolve it later.
2303
2340
const newTask = createTask (
2304
2341
request ,
@@ -2344,15 +2381,6 @@ function renderModel(
2344
2381
}
2345
2382
}
2346
2383
2347
- if ( request . status === ABORTING ) {
2348
- task . status = ABORTED ;
2349
- const errorId : number = ( request . fatalError : any ) ;
2350
- if ( wasReactNode ) {
2351
- return serializeLazyID ( errorId ) ;
2352
- }
2353
- return serializeByValueID ( errorId ) ;
2354
- }
2355
-
2356
2384
// Restore the context. We assume that this will be restored by the inner
2357
2385
// functions in case nothing throws so we don't use "finally" here.
2358
2386
task . keyPath = prevKeyPath ;
@@ -3820,6 +3848,22 @@ function retryTask(request: Request, task: Task): void {
3820
3848
request.abortableTasks.delete(task);
3821
3849
task.status = COMPLETED;
3822
3850
} catch ( thrownValue ) {
3851
+ if ( request . status === ABORTING ) {
3852
+ request . abortableTasks . delete ( task ) ;
3853
+ task . status = ABORTED ;
3854
+ if ( enableHalt && request . type === PRERENDER ) {
3855
+ // When aborting a prerener with halt semantics we don't emit
3856
+ // anything into the slot for a task that aborts, it remains unresolved
3857
+ request. pendingChunks -- ;
3858
+ } else {
3859
+ // Otherwise we emit an error chunk into the task slot.
3860
+ const errorId : number = ( request . fatalError : any ) ;
3861
+ const model = stringify ( serializeByValueID ( errorId ) ) ;
3862
+ emitModelChunk ( request , task . id , model ) ;
3863
+ }
3864
+ return;
3865
+ }
3866
+
3823
3867
const x =
3824
3868
thrownValue === SuspenseException
3825
3869
? // This is a special type of exception used for Suspense. For historical
@@ -3832,14 +3876,6 @@ function retryTask(request: Request, task: Task): void {
3832
3876
if ( typeof x === 'object ' && x !== null ) {
3833
3877
// $FlowFixMe[method-unbinding]
3834
3878
if ( typeof x . then === 'function' ) {
3835
- if ( request . status === ABORTING ) {
3836
- request . abortableTasks . delete ( task ) ;
3837
- task . status = ABORTED ;
3838
- const errorId : number = ( request . fatalError : any ) ;
3839
- const model = stringify ( serializeByValueID ( errorId ) ) ;
3840
- emitModelChunk ( request , task . id , model ) ;
3841
- return ;
3842
- }
3843
3879
// Something suspended again, let's pick it back up later.
3844
3880
task . status = PENDING ;
3845
3881
task . thenableState = getThenableStateAfterSuspending ( ) ;
@@ -3856,15 +3892,6 @@ function retryTask(request: Request, task: Task): void {
3856
3892
}
3857
3893
}
3858
3894
3859
- if ( request . status === ABORTING ) {
3860
- request . abortableTasks . delete ( task ) ;
3861
- task . status = ABORTED ;
3862
- const errorId : number = ( request . fatalError : any ) ;
3863
- const model = stringify ( serializeByValueID ( errorId ) ) ;
3864
- emitModelChunk ( request , task . id , model ) ;
3865
- return ;
3866
- }
3867
-
3868
3895
request . abortableTasks . delete ( task ) ;
3869
3896
task . status = ERRORED ;
3870
3897
const digest = logRecoverableError ( request , x , task ) ;
@@ -3942,6 +3969,17 @@ function abortTask(task: Task, request: Request, errorId: number): void {
3942
3969
request.completedErrorChunks.push(processedChunk);
3943
3970
}
3944
3971
3972
+ function haltTask ( task : Task , request : Request ) : void {
3973
+ if ( task . status === RENDERING ) {
3974
+ // this task will be halted by the render
3975
+ return ;
3976
+ }
3977
+ task.status = ABORTED;
3978
+ // We don't actually emit anything for this task id because we are intentionally
3979
+ // leaving the reference unfulfilled.
3980
+ request.pendingChunks--;
3981
+ }
3982
+
3945
3983
function flushCompletedChunks (
3946
3984
request : Request ,
3947
3985
destination : Destination ,
@@ -4087,12 +4125,6 @@ export function abort(request: Request, reason: mixed): void {
4087
4125
}
4088
4126
const abortableTasks = request . abortableTasks ;
4089
4127
if ( abortableTasks . size > 0) {
4090
- // We have tasks to abort. We'll emit one error row and then emit a reference
4091
- // to that row from every row that's still remaining if we are rendering. If we
4092
- // are prerendering (and halt semantics are enabled) we will refer to an error row
4093
- // but not actually emit it so the reciever can at that point rather than error.
4094
- const errorId = request . nextChunkId ++ ;
4095
- request . fatalError = errorId ;
4096
4128
if (
4097
4129
enablePostpone &&
4098
4130
typeof reason === 'object' &&
@@ -4101,10 +4133,20 @@ export function abort(request: Request, reason: mixed): void {
4101
4133
) {
4102
4134
const postponeInstance : Postpone = ( reason : any ) ;
4103
4135
logPostpone ( request , postponeInstance . message , null ) ;
4104
- if ( ! enableHalt || request . type === PRERENDER ) {
4105
- // When prerendering with halt semantics we omit the referred to postpone.
4136
+ if ( enableHalt && request . type === PRERENDER ) {
4137
+ // When prerendering with halt semantics we simply halt the task
4138
+ // and leave the reference unfulfilled.
4139
+ abortableTasks. forEach ( task => haltTask ( task , request ) ) ;
4140
+ abortableTasks . clear ( ) ;
4141
+ } else {
4142
+ // When rendering we produce a shared postpone chunk and then
4143
+ // fulfill each task with a reference to that chunk.
4144
+ const errorId = request . nextChunkId ++ ;
4145
+ request . fatalError = errorId ;
4106
4146
request . pendingChunks ++ ;
4107
4147
emitPostponeChunk ( request , errorId , postponeInstance ) ;
4148
+ abortableTasks . forEach ( task => abortTask ( task , request , errorId ) ) ;
4149
+ abortableTasks . clear ( ) ;
4108
4150
}
4109
4151
} else {
4110
4152
const error =
@@ -4120,14 +4162,22 @@ export function abort(request: Request, reason: mixed): void {
4120
4162
)
4121
4163
: reason ;
4122
4164
const digest = logRecoverableError ( request , error , null ) ;
4123
- if ( ! enableHalt || request . type === RENDER ) {
4124
- // When prerendering with halt semantics we omit the referred to error.
4165
+ if ( enableHalt && request . type === PRERENDER ) {
4166
+ // When prerendering with halt semantics we simply halt the task
4167
+ // and leave the reference unfulfilled.
4168
+ abortableTasks . forEach ( task => haltTask ( task , request ) ) ;
4169
+ abortableTasks . clear ( ) ;
4170
+ } else {
4171
+ // When rendering we produce a shared error chunk and then
4172
+ // fulfill each task with a reference to that chunk.
4173
+ const errorId = request . nextChunkId ++ ;
4174
+ request . fatalError = errorId ;
4125
4175
request . pendingChunks ++ ;
4126
4176
emitErrorChunk ( request , errorId , digest , error ) ;
4177
+ abortableTasks . forEach ( task => abortTask ( task , request , errorId ) ) ;
4178
+ abortableTasks . clear ( ) ;
4127
4179
}
4128
4180
}
4129
- abortableTasks . forEach ( task => abortTask ( task , request , errorId ) ) ;
4130
- abortableTasks . clear ( ) ;
4131
4181
const onAllReady = request . onAllReady ;
4132
4182
onAllReady ( ) ;
4133
4183
}
0 commit comments