@@ -753,30 +753,32 @@ function serializeReadableStream(
753
753
}
754
754
aborted = true ;
755
755
request . abortListeners . delete ( error ) ;
756
-
757
- let cancelWith : mixed ;
758
- if ( enableHalt && request . fatalError === haltSymbol ) {
759
- cancelWith = reason ;
760
- } else if (
756
+ if (
761
757
enablePostpone &&
762
758
typeof reason === 'object' &&
763
759
reason !== null &&
764
760
( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
765
761
) {
766
- cancelWith = reason ;
767
762
const postponeInstance : Postpone = ( reason : any ) ;
768
763
logPostpone ( request , postponeInstance . message , streamTask ) ;
769
- emitPostponeChunk ( request , streamTask . id , postponeInstance ) ;
770
- enqueueFlush ( request ) ;
764
+ if ( enableHalt && request . fatalError === haltSymbol ) {
765
+ request . pendingChunks -- ;
766
+ } else {
767
+ emitPostponeChunk ( request , streamTask . id , postponeInstance ) ;
768
+ enqueueFlush ( request ) ;
769
+ }
771
770
} else {
772
- cancelWith = reason ;
773
771
const digest = logRecoverableError ( request , reason , streamTask ) ;
774
- emitErrorChunk ( request , streamTask . id , digest , reason ) ;
775
- enqueueFlush ( request ) ;
772
+ if ( enableHalt && request . fatalError === haltSymbol ) {
773
+ request . pendingChunks -- ;
774
+ } else {
775
+ emitErrorChunk ( request , streamTask . id , digest , reason ) ;
776
+ enqueueFlush ( request ) ;
777
+ }
776
778
}
777
779
778
780
// $FlowFixMe should be able to pass mixed
779
- reader . cancel ( cancelWith ) . then ( error , error ) ;
781
+ reader . cancel ( reason ) . then ( error , error ) ;
780
782
}
781
783
782
784
request . abortListeners . add ( error ) ;
@@ -880,30 +882,33 @@ function serializeAsyncIterable(
880
882
}
881
883
aborted = true ;
882
884
request . abortListeners . delete ( error ) ;
883
- let throwWith: mixed;
884
- if (enableHalt && request . fatalError === haltSymbol ) {
885
- throwWith = reason ;
886
- } else if (
885
+ if (
887
886
enablePostpone &&
888
887
typeof reason === 'object' &&
889
888
reason !== null &&
890
889
( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
891
890
) {
892
- throwWith = reason ;
893
891
const postponeInstance : Postpone = ( reason : any ) ;
894
892
logPostpone ( request , postponeInstance . message , streamTask ) ;
895
- emitPostponeChunk ( request , streamTask . id , postponeInstance ) ;
896
- enqueueFlush ( request ) ;
893
+ if ( enableHalt && request . fatalError === haltSymbol ) {
894
+ request . pendingChunks -- ;
895
+ } else {
896
+ emitPostponeChunk ( request , streamTask . id , postponeInstance ) ;
897
+ enqueueFlush ( request ) ;
898
+ }
897
899
} else {
898
- throwWith = reason ;
899
900
const digest = logRecoverableError ( request , reason , streamTask ) ;
900
- emitErrorChunk ( request , streamTask . id , digest , reason ) ;
901
- enqueueFlush ( request ) ;
901
+ if ( enableHalt && request . fatalError === haltSymbol ) {
902
+ request . pendingChunks -- ;
903
+ } else {
904
+ emitErrorChunk ( request , streamTask . id , digest , reason ) ;
905
+ enqueueFlush ( request ) ;
906
+ }
902
907
}
903
908
if ( typeof ( iterator : any ) . throw === 'function' ) {
904
909
// The iterator protocol doesn't necessarily include this but a generator do.
905
910
// $FlowFixMe should be able to pass mixed
906
- iterator . throw ( throwWith ) . then ( error , error ) ;
911
+ iterator . throw ( reason ) . then ( error , error ) ;
907
912
}
908
913
}
909
914
request . abortListeners . add ( error ) ;
@@ -2095,18 +2100,31 @@ function serializeBlob(request: Request, blob: Blob): string {
2095
2100
}
2096
2101
aborted = true ;
2097
2102
request . abortListeners . delete ( error ) ;
2098
- let cancelWith : mixed ;
2099
- if ( enableHalt && request . fatalError === haltSymbol ) {
2100
- cancelWith = reason ;
2103
+ if (
2104
+ enablePostpone &&
2105
+ typeof reason === 'object' &&
2106
+ reason !== null &&
2107
+ ( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
2108
+ ) {
2109
+ const postponeInstance : Postpone = ( reason : any ) ;
2110
+ logPostpone ( request , postponeInstance . message , newTask ) ;
2111
+ if ( enableHalt && request . fatalError === haltSymbol ) {
2112
+ request . pendingChunks -- ;
2113
+ } else {
2114
+ emitPostponeChunk ( request , newTask . id , postponeInstance ) ;
2115
+ enqueueFlush ( request ) ;
2116
+ }
2101
2117
} else {
2102
- cancelWith = reason ;
2103
2118
const digest = logRecoverableError ( request , reason , newTask ) ;
2104
- emitErrorChunk ( request , newTask . id , digest , reason ) ;
2105
- request . abortableTasks . delete ( newTask ) ;
2106
- enqueueFlush ( request ) ;
2119
+ if ( enableHalt && request . fatalError === haltSymbol ) {
2120
+ request . pendingChunks -- ;
2121
+ } else {
2122
+ emitErrorChunk ( request , newTask . id , digest , reason ) ;
2123
+ enqueueFlush ( request ) ;
2124
+ }
2107
2125
}
2108
2126
// $FlowFixMe should be able to pass mixed
2109
- reader . cancel ( cancelWith ) . then ( error , error ) ;
2127
+ reader . cancel ( reason ) . then ( error , error ) ;
2110
2128
}
2111
2129
2112
2130
request . abortListeners . add ( error ) ;
@@ -3998,14 +4016,15 @@ export function stopFlowing(request: Request): void {
3998
4016
3999
4017
// This is called to early terminate a request. It creates an error at all pending tasks.
4000
4018
export function abort ( request : Request , reason : mixed ) : void {
4019
+ if ( request . status === OPEN ) {
4020
+ request . status = ABORTING ;
4021
+ }
4001
4022
try {
4002
- if ( request . status === OPEN ) {
4003
- request . status = ABORTING ;
4004
- }
4005
4023
const abortableTasks = request . abortableTasks ;
4006
4024
// We have tasks to abort. We'll emit one error row and then emit a reference
4007
4025
// to that row from every row that's still remaining.
4008
4026
if ( abortableTasks . size > 0 ) {
4027
+ request . status = ABORTING ;
4009
4028
request . pendingChunks ++ ;
4010
4029
const errorId = request . nextChunkId ++ ;
4011
4030
request . fatalError = errorId ;
@@ -4019,54 +4038,14 @@ export function abort(request: Request, reason: mixed): void {
4019
4038
logPostpone ( request , postponeInstance . message , null ) ;
4020
4039
emitPostponeChunk ( request , errorId , postponeInstance ) ;
4021
4040
} else {
4022
- const error =
4023
- reason === undefined
4024
- ? new Error (
4025
- 'The render was aborted by the server without a reason.' ,
4026
- )
4027
- : typeof reason === 'object' &&
4028
- reason !== null &&
4029
- typeof reason . then === 'function'
4030
- ? new Error (
4031
- 'The render was aborted by the server with a promise.' ,
4032
- )
4033
- : reason ;
4041
+ const error = resolveAbortError ( reason ) ;
4034
4042
const digest = logRecoverableError ( request , error , null ) ;
4035
4043
emitErrorChunk ( request , errorId , digest , error ) ;
4036
4044
}
4037
4045
abortableTasks . forEach ( task = > abortTask(task, request, errorId));
4038
4046
abortableTasks.clear();
4039
4047
}
4040
- const abortListeners = request . abortListeners ;
4041
- if ( abortListeners . size > 0 ) {
4042
- let error ;
4043
- if (
4044
- enablePostpone &&
4045
- typeof reason === 'object' &&
4046
- reason !== null &&
4047
- ( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
4048
- ) {
4049
- // We aborted with a Postpone but since we're passing this to an
4050
- // external handler, passing this object would leak it outside React.
4051
- // We create an alternative reason for it instead.
4052
- error = new Error ( 'The render was aborted due to being postponed.' ) ;
4053
- } else {
4054
- error =
4055
- reason === undefined
4056
- ? new Error (
4057
- 'The render was aborted by the server without a reason.' ,
4058
- )
4059
- : typeof reason === 'object' &&
4060
- reason !== null &&
4061
- typeof reason . then === 'function'
4062
- ? new Error (
4063
- 'The render was aborted by the server with a promise.' ,
4064
- )
4065
- : reason ;
4066
- }
4067
- abortListeners.forEach(callback => callback ( error ) ) ;
4068
- abortListeners . clear ( ) ;
4069
- }
4048
+ abortAnyListeners ( reason , request . abortListeners ) ;
4070
4049
if ( request . destination !== null ) {
4071
4050
flushCompletedChunks ( request , request . destination ) ;
4072
4051
}
@@ -4082,23 +4061,32 @@ const haltSymbol = Symbol('halt');
4082
4061
// This is called to stop rendering without erroring. All unfinished work is represented Promises
4083
4062
// that never resolve.
4084
4063
export function halt ( request : Request , reason : mixed ) : void {
4064
+ if ( request . status === OPEN ) {
4065
+ request . status = ABORTING ;
4066
+ }
4067
+ request.fatalError = haltSymbol;
4085
4068
try {
4086
- if ( request . status === OPEN ) {
4087
- request . status = ABORTING ;
4088
- }
4089
- request.fatalError = haltSymbol;
4090
4069
const abortableTasks = request . abortableTasks ;
4091
- // We have tasks to abort. We'll emit one error row and then emit a reference
4092
- // to that row from every row that's still remaining.
4093
4070
if ( abortableTasks . size > 0 ) {
4071
+ // We have tasks to halt. We will log the error or postpone but we don't
4072
+ // emit an error or postpone chunk. Instead we will emit a reference that
4073
+ // never resolves on the client.
4074
+ if (
4075
+ enablePostpone &&
4076
+ typeof reason === 'object' &&
4077
+ reason !== null &&
4078
+ ( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
4079
+ ) {
4080
+ const postponeInstance : Postpone = ( reason : any ) ;
4081
+ logPostpone ( request , postponeInstance . message , null ) ;
4082
+ } else {
4083
+ const error = resolveAbortError ( reason ) ;
4084
+ logRecoverableError ( request , error , null ) ;
4085
+ }
4094
4086
abortableTasks . forEach ( task => haltTask ( task , request ) ) ;
4095
4087
abortableTasks . clear ( ) ;
4096
4088
}
4097
- const abortListeners = request.abortListeners;
4098
- if (abortListeners.size > 0 ) {
4099
- abortListeners . forEach ( callback => callback ( reason ) ) ;
4100
- abortListeners . clear ( ) ;
4101
- }
4089
+ abortAnyListeners(reason, request.abortListeners);
4102
4090
if (request.destination !== null) {
4103
4091
flushCompletedChunks ( request , request . destination ) ;
4104
4092
}
@@ -4109,6 +4097,47 @@ export function halt(request: Request, reason: mixed): void {
4109
4097
}
4110
4098
}
4111
4099
4100
+ function resolveAbortError ( reason : mixed ) : mixed {
4101
+ return reason === undefined
4102
+ ? new Error ( 'The render was aborted by the server without a reason.' )
4103
+ : typeof reason === 'object' &&
4104
+ reason !== null &&
4105
+ typeof reason . then === 'function'
4106
+ ? new Error ( 'The render was aborted by the server with a promise.' )
4107
+ : reason ;
4108
+ }
4109
+
4110
+ function abortAnyListeners(
4111
+ reason: mixed,
4112
+ listeners: Set< ( reason : mixed ) => void > ,
4113
+ ) {
4114
+ if ( listeners . size > 0 ) {
4115
+ let error ;
4116
+ if (
4117
+ enablePostpone &&
4118
+ typeof reason === 'object' &&
4119
+ reason !== null &&
4120
+ ( reason : any ) . $$typeof === REACT_POSTPONE_TYPE
4121
+ ) {
4122
+ // We aborted with a Postpone but since we're passing this to an
4123
+ // external handler, passing this object would leak it outside React.
4124
+ // We create an alternative reason for it instead.
4125
+ error = new Error ( 'The render was aborted due to being postponed.' ) ;
4126
+ } else {
4127
+ error =
4128
+ reason === undefined
4129
+ ? new Error ( 'The render was aborted by the server without a reason.' )
4130
+ : typeof reason === 'object' &&
4131
+ reason !== null &&
4132
+ typeof reason . then === 'function'
4133
+ ? new Error ( 'The render was aborted by the server with a promise.' )
4134
+ : reason ;
4135
+ }
4136
+ listeners.forEach(callback => callback ( error ) ) ;
4137
+ listeners . clear ( ) ;
4138
+ }
4139
+ }
4140
+
4112
4141
function allReady ( request : Request ) {
4113
4142
const onAllReady = request . onAllReady ;
4114
4143
onAllReady ( ) ;
0 commit comments