@@ -143,6 +143,7 @@ import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
143
143
export { default as rendererVersion } from 'shared/ReactVersion' ;
144
144
145
145
import noop from 'shared/noop' ;
146
+ import estimateBandwidth from './estimateBandwidth' ;
146
147
147
148
export const rendererPackageName = 'react-dom' ;
148
149
export const extraDevToolsConfig = null ;
@@ -5907,6 +5908,7 @@ type SuspendedState = {
5907
5908
stylesheets : null | Map < StylesheetResource , HoistableRoot > ,
5908
5909
count : number , // suspensey css and active view transitions
5909
5910
imgCount : number , // suspensey images
5911
+ imgBytes : number , // number of bytes we estimate needing to download
5910
5912
waitingForImages : boolean , // false when we're no longer blocking on images
5911
5913
unsuspend : null | ( ( ) => void ) ,
5912
5914
} ;
@@ -5917,6 +5919,7 @@ export function startSuspendingCommit(): void {
5917
5919
stylesheets : null ,
5918
5920
count : 0 ,
5919
5921
imgCount : 0 ,
5922
+ imgBytes : 0 ,
5920
5923
waitingForImages : true ,
5921
5924
// We use a noop function when we begin suspending because if possible we want the
5922
5925
// waitfor step to finish synchronously. If it doesn't we'll return a function to
@@ -5926,10 +5929,6 @@ export function startSuspendingCommit(): void {
5926
5929
} ;
5927
5930
}
5928
5931
5929
- const SUSPENSEY_STYLESHEET_TIMEOUT = 60000 ;
5930
-
5931
- const SUSPENSEY_IMAGE_TIMEOUT = 500 ;
5932
-
5933
5932
export function suspendInstance (
5934
5933
instance : Instance ,
5935
5934
type : Type ,
@@ -5953,6 +5952,19 @@ export function suspendInstance(
5953
5952
// The loading should have already started at this point, so it should be enough to
5954
5953
// just call decode() which should also wait for the data to finish loading.
5955
5954
state . imgCount ++ ;
5955
+ // Estimate the byte size that we're about to download based on the width/height
5956
+ // specified in the props. This is best practice to know ahead of time but if it's
5957
+ // unspecified we'll fallback to a guess of 100x100 pixels.
5958
+ if ( ! ( instance : any ) . complete ) {
5959
+ const width : number = ( instance : any ) . width || 100 ;
5960
+ const height : number = ( instance : any ) . height || 100 ;
5961
+ const pixelRatio : number =
5962
+ typeof devicePixelRatio === 'number' ? devicePixelRatio : 1 ;
5963
+ const AVERAGE_BYTE_PER_PIXEL = 0.25 ;
5964
+ const pixelsToDownload =
5965
+ width * height * pixelRatio * AVERAGE_BYTE_PER_PIXEL ;
5966
+ state . imgBytes += pixelsToDownload ;
5967
+ }
5956
5968
const ping = onUnsuspendImg . bind ( state ) ;
5957
5969
// $FlowFixMe[prop-missing]
5958
5970
instance . decode ( ) . then ( ping , ping ) ;
@@ -6070,6 +6082,14 @@ export function suspendOnActiveViewTransition(rootContainer: Container): void {
6070
6082
activeViewTransition . finished . then ( ping , ping ) ;
6071
6083
}
6072
6084
6085
+ const SUSPENSEY_STYLESHEET_TIMEOUT = 60000 ;
6086
+
6087
+ const SUSPENSEY_IMAGE_TIMEOUT = 800 ;
6088
+
6089
+ const SUSPENSEY_IMAGE_TIME_ESTIMATE = 500 ;
6090
+
6091
+ let estimatedBytesWithinLimit : number = 0 ;
6092
+
6073
6093
export function waitForCommitToBeReady (
6074
6094
timeoutOffset : number ,
6075
6095
) : null | ( ( ( ) => void ) => ( ) => void ) {
@@ -6109,6 +6129,18 @@ export function waitForCommitToBeReady(
6109
6129
}
6110
6130
} , SUSPENSEY_STYLESHEET_TIMEOUT + timeoutOffset ) ;
6111
6131
6132
+ if ( state . imgBytes > 0 && estimatedBytesWithinLimit === 0 ) {
6133
+ // Estimate how many bytes we can download in 500ms.
6134
+ const mbps = estimateBandwidth ( ) ;
6135
+ estimatedBytesWithinLimit = mbps * 125 * SUSPENSEY_IMAGE_TIME_ESTIMATE ;
6136
+ }
6137
+ // If we have more images to download than we expect to fit in the timeout, then
6138
+ // don't wait for images longer than 50ms. The 50ms lets us still do decoding and
6139
+ // hitting caches if it turns out that they're already in the HTTP cache.
6140
+ const imgTimeout =
6141
+ state . imgBytes > estimatedBytesWithinLimit
6142
+ ? 50
6143
+ : SUSPENSEY_IMAGE_TIMEOUT ;
6112
6144
const imgTimer = setTimeout ( ( ) => {
6113
6145
// We're no longer blocked on images. If CSS resolves after this we can commit.
6114
6146
state . waitingForImages = false ;
@@ -6122,7 +6154,7 @@ export function waitForCommitToBeReady(
6122
6154
unsuspend ( ) ;
6123
6155
}
6124
6156
}
6125
- } , SUSPENSEY_IMAGE_TIMEOUT + timeoutOffset ) ;
6157
+ } , imgTimeout + timeoutOffset ) ;
6126
6158
6127
6159
state . unsuspend = commit ;
6128
6160
0 commit comments