@@ -1014,7 +1014,6 @@ function getStreamUsage(
10141014
10151015 return streamUsage ;
10161016}
1017-
10181017/**
10191018 * Complete a async iterator value by completing the result and calling
10201019 * recursively until all the results are completed.
@@ -1033,10 +1032,87 @@ async function completeAsyncIteratorValue(
10331032 const completedResults : Array < unknown > = [ ] ;
10341033 const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
10351034 let index = 0 ;
1036- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
10371035 // eslint-disable-next-line no-constant-condition
10381036 while ( true ) {
1039- if ( streamUsage && index >= streamUsage . initialCount ) {
1037+ const itemPath = addPath ( path , index , undefined ) ;
1038+ let iteration ;
1039+ try {
1040+ // eslint-disable-next-line no-await-in-loop
1041+ iteration = await asyncIterator . next ( ) ;
1042+ } catch ( rawError ) {
1043+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1044+ }
1045+
1046+ if ( iteration . done ) {
1047+ break ;
1048+ }
1049+
1050+ const item = iteration . value ;
1051+ // TODO: add test case for asyncIterator returning a promise
1052+ /* c8 ignore start */
1053+ if ( isPromise ( item ) ) {
1054+ completedResults . push (
1055+ completePromisedListItemValue (
1056+ item ,
1057+ acc ,
1058+ exeContext ,
1059+ itemType ,
1060+ fieldGroup ,
1061+ info ,
1062+ itemPath ,
1063+ incrementalContext ,
1064+ deferMap ,
1065+ ) ,
1066+ ) ;
1067+ containsPromise = true ;
1068+ } else if (
1069+ /* c8 ignore stop */
1070+ completeListItemValue (
1071+ item ,
1072+ completedResults ,
1073+ acc ,
1074+ exeContext ,
1075+ itemType ,
1076+ fieldGroup ,
1077+ info ,
1078+ itemPath ,
1079+ incrementalContext ,
1080+ deferMap ,
1081+ )
1082+ ) {
1083+ containsPromise = true ;
1084+ }
1085+ index ++ ;
1086+ }
1087+
1088+ return containsPromise
1089+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1090+ : acc ;
1091+ }
1092+
1093+ /**
1094+ * Complete a async iterator value by completing the result and calling
1095+ * recursively until all the results are completed.
1096+ */
1097+ async function completeAsyncIteratorValueWithPossibleStream (
1098+ exeContext : ExecutionContext ,
1099+ itemType : GraphQLOutputType ,
1100+ fieldGroup : FieldGroup ,
1101+ info : GraphQLResolveInfo ,
1102+ path : Path ,
1103+ asyncIterator : AsyncIterator < unknown > ,
1104+ streamUsage : StreamUsage ,
1105+ incrementalContext : IncrementalContext | undefined ,
1106+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1107+ ) : Promise < GraphQLResult < ReadonlyArray < unknown > > > {
1108+ let containsPromise = false ;
1109+ const completedResults : Array < unknown > = [ ] ;
1110+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1111+ let index = 0 ;
1112+ const initialCount = streamUsage . initialCount ;
1113+ // eslint-disable-next-line no-constant-condition
1114+ while ( true ) {
1115+ if ( index >= initialCount ) {
10401116 const streamRecord = new StreamRecord ( {
10411117 label : streamUsage . label ,
10421118 path,
@@ -1147,17 +1223,32 @@ function completeListValue(
11471223 deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
11481224) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
11491225 const itemType = returnType . ofType ;
1226+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
11501227
11511228 if ( isAsyncIterable ( result ) ) {
11521229 const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
11531230
1154- return completeAsyncIteratorValue (
1231+ if ( streamUsage === undefined ) {
1232+ return completeAsyncIteratorValue (
1233+ exeContext ,
1234+ itemType ,
1235+ fieldGroup ,
1236+ info ,
1237+ path ,
1238+ asyncIterator ,
1239+ incrementalContext ,
1240+ deferMap ,
1241+ ) ;
1242+ }
1243+
1244+ return completeAsyncIteratorValueWithPossibleStream (
11551245 exeContext ,
11561246 itemType ,
11571247 fieldGroup ,
11581248 info ,
11591249 path ,
11601250 asyncIterator ,
1251+ streamUsage ,
11611252 incrementalContext ,
11621253 deferMap ,
11631254 ) ;
@@ -1169,13 +1260,27 @@ function completeListValue(
11691260 ) ;
11701261 }
11711262
1172- return completeIterableValue (
1263+ if ( streamUsage === undefined ) {
1264+ return completeIterableValue (
1265+ exeContext ,
1266+ itemType ,
1267+ fieldGroup ,
1268+ info ,
1269+ path ,
1270+ result ,
1271+ incrementalContext ,
1272+ deferMap ,
1273+ ) ;
1274+ }
1275+
1276+ return completeIterableValueWithPossibleStream (
11731277 exeContext ,
11741278 itemType ,
11751279 fieldGroup ,
11761280 info ,
11771281 path ,
11781282 result ,
1283+ streamUsage ,
11791284 incrementalContext ,
11801285 deferMap ,
11811286 ) ;
@@ -1197,13 +1302,74 @@ function completeIterableValue(
11971302 const completedResults : Array < unknown > = [ ] ;
11981303 const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
11991304 let index = 0 ;
1200- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1305+ for ( const item of items ) {
1306+ // No need to modify the info object containing the path,
1307+ // since from here on it is not ever accessed by resolver functions.
1308+ const itemPath = addPath ( path , index , undefined ) ;
1309+
1310+ if ( isPromise ( item ) ) {
1311+ completedResults . push (
1312+ completePromisedListItemValue (
1313+ item ,
1314+ acc ,
1315+ exeContext ,
1316+ itemType ,
1317+ fieldGroup ,
1318+ info ,
1319+ itemPath ,
1320+ incrementalContext ,
1321+ deferMap ,
1322+ ) ,
1323+ ) ;
1324+ containsPromise = true ;
1325+ } else if (
1326+ completeListItemValue (
1327+ item ,
1328+ completedResults ,
1329+ acc ,
1330+ exeContext ,
1331+ itemType ,
1332+ fieldGroup ,
1333+ info ,
1334+ itemPath ,
1335+ incrementalContext ,
1336+ deferMap ,
1337+ )
1338+ ) {
1339+ containsPromise = true ;
1340+ }
1341+ index ++ ;
1342+ }
1343+
1344+ return containsPromise
1345+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1346+ : acc ;
1347+ }
1348+
1349+ function completeIterableValueWithPossibleStream (
1350+ exeContext : ExecutionContext ,
1351+ itemType : GraphQLOutputType ,
1352+ fieldGroup : FieldGroup ,
1353+ info : GraphQLResolveInfo ,
1354+ path : Path ,
1355+ items : Iterable < unknown > ,
1356+ streamUsage : StreamUsage ,
1357+ incrementalContext : IncrementalContext | undefined ,
1358+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1359+ ) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
1360+ // This is specified as a simple map, however we're optimizing the path
1361+ // where the list contains no Promises by avoiding creating another Promise.
1362+ let containsPromise = false ;
1363+ const completedResults : Array < unknown > = [ ] ;
1364+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1365+ let index = 0 ;
1366+ const initialCount = streamUsage . initialCount ;
12011367 const iterator = items [ Symbol . iterator ] ( ) ;
12021368 let iteration = iterator . next ( ) ;
12031369 while ( ! iteration . done ) {
12041370 const item = iteration . value ;
12051371
1206- if ( streamUsage && index >= streamUsage . initialCount ) {
1372+ if ( index >= initialCount ) {
12071373 const streamRecord = new StreamRecord ( {
12081374 label : streamUsage . label ,
12091375 path,
0 commit comments