@@ -89,6 +89,7 @@ import {
89
89
requestStorage ,
90
90
createHints ,
91
91
initAsyncDebugInfo ,
92
+ markAsyncSequenceRootTask ,
92
93
getCurrentAsyncSequence ,
93
94
parseStackTrace ,
94
95
supportsComponentStorage ,
@@ -149,7 +150,13 @@ import binaryToComparableString from 'shared/binaryToComparableString';
149
150
150
151
import { SuspenseException , getSuspendedThenable } from './ReactFlightThenable' ;
151
152
152
- import { IO_NODE , PROMISE_NODE , AWAIT_NODE } from './ReactFlightAsyncSequence' ;
153
+ import {
154
+ IO_NODE ,
155
+ PROMISE_NODE ,
156
+ AWAIT_NODE ,
157
+ UNRESOLVED_AWAIT_NODE ,
158
+ UNRESOLVED_PROMISE_NODE ,
159
+ } from './ReactFlightAsyncSequence' ;
153
160
154
161
// DEV-only set containing internal objects that should not be limited and turned into getters.
155
162
const doNotLimit : WeakSet < Reference > = __DEV__ ? new WeakSet() : (null: any);
@@ -1879,6 +1886,9 @@ function visitAsyncNode(
1879
1886
case IO_NODE : {
1880
1887
return node ;
1881
1888
}
1889
+ case UNRESOLVED_PROMISE_NODE : {
1890
+ return null ;
1891
+ }
1882
1892
case PROMISE_NODE : {
1883
1893
if ( node . end < cutOff ) {
1884
1894
// This was already resolved when we started this sequence. It must have been
@@ -1888,6 +1898,7 @@ function visitAsyncNode(
1888
1898
return null ;
1889
1899
}
1890
1900
const awaited = node . awaited ;
1901
+ let match = null ;
1891
1902
if ( awaited !== null ) {
1892
1903
const ioNode = visitAsyncNode ( request , task , awaited , cutOff , visited ) ;
1893
1904
if ( ioNode !== null ) {
@@ -1907,72 +1918,104 @@ function visitAsyncNode(
1907
1918
// If we haven't defined an end time, use the resolve of the outer Promise.
1908
1919
ioNode . end = node . end ;
1909
1920
}
1910
- return ioNode ;
1921
+ match = ioNode ;
1922
+ } else {
1923
+ match = node ;
1911
1924
}
1912
- return node ;
1913
1925
}
1914
1926
}
1915
- return null ;
1927
+ // We need to forward after we visit awaited nodes because what ever I/O we requested that's
1928
+ // the thing that generated this node and its virtual children.
1929
+ const debugInfo = node . debugInfo ;
1930
+ if ( debugInfo !== null ) {
1931
+ forwardDebugInfo ( request , task . id , debugInfo ) ;
1932
+ }
1933
+ return match ;
1916
1934
}
1935
+ case UNRESOLVED_AWAIT_NODE :
1936
+ // We could be inside the .then() which is about to resolve this node.
1937
+ // TODO: We could call emitAsyncSequence in a microtask to avoid this issue.
1938
+ // Fallthrough to the resolved path.
1917
1939
case AWAIT_NODE : {
1918
1940
const awaited = node . awaited ;
1941
+ let match = null ;
1919
1942
if ( awaited !== null ) {
1920
1943
const ioNode = visitAsyncNode ( request , task , awaited , cutOff , visited ) ;
1921
1944
if ( ioNode !== null ) {
1922
- if ( node . end < 0 ) {
1945
+ let endTime : number ;
1946
+ if ( node . tag === UNRESOLVED_AWAIT_NODE ) {
1923
1947
// If we haven't defined an end time, use the resolve of the inner Promise.
1924
1948
// This can happen because the ping gets invoked before the await gets resolved.
1925
1949
if ( ioNode . end < node . start ) {
1926
1950
// If we're awaiting a resolved Promise it could have finished before we started.
1927
- node . end = node . start ;
1951
+ endTime = node . start ;
1928
1952
} else {
1929
- node . end = ioNode . end ;
1953
+ endTime = ioNode . end ;
1930
1954
}
1955
+ } else {
1956
+ endTime = node . end ;
1931
1957
}
1932
- if ( node . end < cutOff ) {
1958
+ if ( endTime < cutOff ) {
1933
1959
// This was already resolved when we started this sequence. It must have been
1934
1960
// part of a different component.
1935
1961
// TODO: Think of some other way to exclude irrelevant data since if we awaited
1936
1962
// a cached promise, we should still log this component as being dependent on that data.
1937
- return null ;
1938
- }
1939
-
1940
- const stack = filterStackTrace (
1941
- request ,
1942
- parseStackTrace ( node . stack , 1 ) ,
1943
- ) ;
1944
- if ( stack . length === 0 ) {
1945
- // If this await was fully filtered out, then it was inside third party code
1946
- // such as in an external library. We return the I/O node and try another await.
1947
- return ioNode ;
1948
- }
1949
- // Outline the IO node.
1950
- serializeIONode ( request , ioNode ) ;
1951
- // We log the environment at the time when the last promise pigned ping which may
1952
- // be later than what the environment was when we actually started awaiting.
1953
- const env = ( 0 , request . environmentName ) ( ) ;
1954
- if ( node . start <= cutOff ) {
1955
- // If this was an await that started before this sequence but finished after,
1956
- // then we clamp it to the start of this sequence. We don't need to emit a time
1957
- // TODO: Typically we'll already have a previous time stamp with the cutOff time
1958
- // so we shouldn't need to emit another one. But not always.
1959
- emitTimingChunk ( request , task . id , cutOff ) ;
1960
1963
} else {
1961
- emitTimingChunk ( request , task . id , node . start ) ;
1964
+ const stack = filterStackTrace (
1965
+ request ,
1966
+ parseStackTrace ( node . stack , 1 ) ,
1967
+ ) ;
1968
+ if ( stack . length === 0 ) {
1969
+ // If this await was fully filtered out, then it was inside third party code
1970
+ // such as in an external library. We return the I/O node and try another await.
1971
+ match = ioNode ;
1972
+ } else {
1973
+ // Outline the IO node.
1974
+ if ( ioNode . end < 0 ) {
1975
+ ioNode . end = endTime ;
1976
+ }
1977
+ serializeIONode ( request , ioNode ) ;
1978
+ // We log the environment at the time when the last promise pigned ping which may
1979
+ // be later than what the environment was when we actually started awaiting.
1980
+ const env = ( 0 , request . environmentName ) ( ) ;
1981
+ if ( node . start <= cutOff ) {
1982
+ // If this was an await that started before this sequence but finished after,
1983
+ // then we clamp it to the start of this sequence. We don't need to emit a time
1984
+ // TODO: Typically we'll already have a previous time stamp with the cutOff time
1985
+ // so we shouldn't need to emit another one. But not always.
1986
+ emitTimingChunk ( request , task . id , cutOff ) ;
1987
+ } else {
1988
+ emitTimingChunk ( request , task . id , node . start ) ;
1989
+ }
1990
+ // Then emit a reference to us awaiting it in the current task.
1991
+ request . pendingChunks ++ ;
1992
+ emitDebugChunk ( request , task . id , {
1993
+ awaited : ( ( ioNode : any ) : ReactIOInfo ) , // This is deduped by this reference.
1994
+ env : env ,
1995
+ owner : node . owner ,
1996
+ stack : stack ,
1997
+ } ) ;
1998
+ emitTimingChunk ( request , task . id , node . end ) ;
1999
+ }
1962
2000
}
1963
- // Then emit a reference to us awaiting it in the current task.
1964
- request . pendingChunks ++ ;
1965
- emitDebugChunk ( request , task . id , {
1966
- awaited : ( ( ioNode : any ) : ReactIOInfo ) , // This is deduped by this reference.
1967
- env : env ,
1968
- owner : node . owner ,
1969
- stack : stack ,
1970
- } ) ;
1971
- emitTimingChunk ( request , task . id , node . end ) ;
1972
2001
}
1973
2002
}
1974
- // If we had awaited anything we would have written it now.
1975
- return null ;
2003
+ // We need to forward after we visit awaited nodes because what ever I/O we requested that's
2004
+ // the thing that generated this node and its virtual children.
2005
+ let debugInfo : null | ReactDebugInfo ;
2006
+ if ( node . tag === UNRESOLVED_AWAIT_NODE ) {
2007
+ const promise = node . debugInfo . deref ( ) ;
2008
+ debugInfo =
2009
+ promise === undefined || promise . _debugInfo === undefined
2010
+ ? null
2011
+ : promise . _debugInfo ;
2012
+ } else {
2013
+ debugInfo = node . debugInfo ;
2014
+ }
2015
+ if ( debugInfo !== null ) {
2016
+ forwardDebugInfo ( request , task . id , debugInfo ) ;
2017
+ }
2018
+ return match ;
1976
2019
}
1977
2020
default : {
1978
2021
// eslint-disable-next-line react-internal/prod-error-codes
@@ -4513,6 +4556,8 @@ function tryStreamTask(request: Request, task: Task): void {
4513
4556
}
4514
4557
4515
4558
function performWork ( request : Request ) : void {
4559
+ markAsyncSequenceRootTask ( ) ;
4560
+
4516
4561
const prevDispatcher = ReactSharedInternals . H ;
4517
4562
ReactSharedInternals . H = HooksDispatcher ;
4518
4563
const prevRequest = currentRequest ;
0 commit comments