@@ -1034,7 +1034,6 @@ function getStreamUsage(
10341034
10351035 return streamUsage ;
10361036}
1037-
10381037/**
10391038 * Complete a async iterator value by completing the result and calling
10401039 * recursively until all the results are completed.
@@ -1053,10 +1052,87 @@ async function completeAsyncIteratorValue(
10531052 const completedResults : Array < unknown > = [ ] ;
10541053 const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
10551054 let index = 0 ;
1056- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
10571055 // eslint-disable-next-line no-constant-condition
10581056 while ( true ) {
1059- if ( streamUsage && index >= streamUsage . initialCount ) {
1057+ const itemPath = addPath ( path , index , undefined ) ;
1058+ let iteration ;
1059+ try {
1060+ // eslint-disable-next-line no-await-in-loop
1061+ iteration = await asyncIterator . next ( ) ;
1062+ } catch ( rawError ) {
1063+ throw locatedError ( rawError , toNodes ( fieldGroup ) , pathToArray ( path ) ) ;
1064+ }
1065+
1066+ if ( iteration . done ) {
1067+ break ;
1068+ }
1069+
1070+ const item = iteration . value ;
1071+ // TODO: add test case for asyncIterator returning a promise
1072+ /* c8 ignore start */
1073+ if ( isPromise ( item ) ) {
1074+ completedResults . push (
1075+ completePromisedListItemValue (
1076+ item ,
1077+ acc ,
1078+ exeContext ,
1079+ itemType ,
1080+ fieldGroup ,
1081+ info ,
1082+ itemPath ,
1083+ incrementalContext ,
1084+ deferMap ,
1085+ ) ,
1086+ ) ;
1087+ containsPromise = true ;
1088+ } else if (
1089+ /* c8 ignore stop */
1090+ completeListItemValue (
1091+ item ,
1092+ completedResults ,
1093+ acc ,
1094+ exeContext ,
1095+ itemType ,
1096+ fieldGroup ,
1097+ info ,
1098+ itemPath ,
1099+ incrementalContext ,
1100+ deferMap ,
1101+ )
1102+ ) {
1103+ containsPromise = true ;
1104+ }
1105+ index ++ ;
1106+ }
1107+
1108+ return containsPromise
1109+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1110+ : acc ;
1111+ }
1112+
1113+ /**
1114+ * Complete a async iterator value by completing the result and calling
1115+ * recursively until all the results are completed.
1116+ */
1117+ async function completeAsyncIteratorValueWithPossibleStream (
1118+ exeContext : ExecutionContext ,
1119+ itemType : GraphQLOutputType ,
1120+ fieldGroup : FieldGroup ,
1121+ info : GraphQLResolveInfo ,
1122+ path : Path ,
1123+ asyncIterator : AsyncIterator < unknown > ,
1124+ streamUsage : StreamUsage ,
1125+ incrementalContext : IncrementalContext | undefined ,
1126+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1127+ ) : Promise < GraphQLResult < ReadonlyArray < unknown > > > {
1128+ let containsPromise = false ;
1129+ const completedResults : Array < unknown > = [ ] ;
1130+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1131+ let index = 0 ;
1132+ const initialCount = streamUsage . initialCount ;
1133+ // eslint-disable-next-line no-constant-condition
1134+ while ( true ) {
1135+ if ( index >= initialCount ) {
10601136 const streamRecord = new StreamRecord ( {
10611137 label : streamUsage . label ,
10621138 path,
@@ -1167,17 +1243,32 @@ function completeListValue(
11671243 deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
11681244) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
11691245 const itemType = returnType . ofType ;
1246+ const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
11701247
11711248 if ( isAsyncIterable ( result ) ) {
11721249 const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
11731250
1174- return completeAsyncIteratorValue (
1251+ if ( streamUsage === undefined ) {
1252+ return completeAsyncIteratorValue (
1253+ exeContext ,
1254+ itemType ,
1255+ fieldGroup ,
1256+ info ,
1257+ path ,
1258+ asyncIterator ,
1259+ incrementalContext ,
1260+ deferMap ,
1261+ ) ;
1262+ }
1263+
1264+ return completeAsyncIteratorValueWithPossibleStream (
11751265 exeContext ,
11761266 itemType ,
11771267 fieldGroup ,
11781268 info ,
11791269 path ,
11801270 asyncIterator ,
1271+ streamUsage ,
11811272 incrementalContext ,
11821273 deferMap ,
11831274 ) ;
@@ -1189,13 +1280,27 @@ function completeListValue(
11891280 ) ;
11901281 }
11911282
1192- return completeIterableValue (
1283+ if ( streamUsage === undefined ) {
1284+ return completeIterableValue (
1285+ exeContext ,
1286+ itemType ,
1287+ fieldGroup ,
1288+ info ,
1289+ path ,
1290+ result ,
1291+ incrementalContext ,
1292+ deferMap ,
1293+ ) ;
1294+ }
1295+
1296+ return completeIterableValueWithPossibleStream (
11931297 exeContext ,
11941298 itemType ,
11951299 fieldGroup ,
11961300 info ,
11971301 path ,
11981302 result ,
1303+ streamUsage ,
11991304 incrementalContext ,
12001305 deferMap ,
12011306 ) ;
@@ -1217,13 +1322,74 @@ function completeIterableValue(
12171322 const completedResults : Array < unknown > = [ ] ;
12181323 const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
12191324 let index = 0 ;
1220- const streamUsage = getStreamUsage ( exeContext , fieldGroup , path ) ;
1325+ for ( const item of items ) {
1326+ // No need to modify the info object containing the path,
1327+ // since from here on it is not ever accessed by resolver functions.
1328+ const itemPath = addPath ( path , index , undefined ) ;
1329+
1330+ if ( isPromise ( item ) ) {
1331+ completedResults . push (
1332+ completePromisedListItemValue (
1333+ item ,
1334+ acc ,
1335+ exeContext ,
1336+ itemType ,
1337+ fieldGroup ,
1338+ info ,
1339+ itemPath ,
1340+ incrementalContext ,
1341+ deferMap ,
1342+ ) ,
1343+ ) ;
1344+ containsPromise = true ;
1345+ } else if (
1346+ completeListItemValue (
1347+ item ,
1348+ completedResults ,
1349+ acc ,
1350+ exeContext ,
1351+ itemType ,
1352+ fieldGroup ,
1353+ info ,
1354+ itemPath ,
1355+ incrementalContext ,
1356+ deferMap ,
1357+ )
1358+ ) {
1359+ containsPromise = true ;
1360+ }
1361+ index ++ ;
1362+ }
1363+
1364+ return containsPromise
1365+ ? Promise . all ( completedResults ) . then ( ( resolved ) => [ resolved , acc [ 1 ] ] )
1366+ : acc ;
1367+ }
1368+
1369+ function completeIterableValueWithPossibleStream (
1370+ exeContext : ExecutionContext ,
1371+ itemType : GraphQLOutputType ,
1372+ fieldGroup : FieldGroup ,
1373+ info : GraphQLResolveInfo ,
1374+ path : Path ,
1375+ items : Iterable < unknown > ,
1376+ streamUsage : StreamUsage ,
1377+ incrementalContext : IncrementalContext | undefined ,
1378+ deferMap : ReadonlyMap < DeferUsage , DeferredFragmentRecord > | undefined ,
1379+ ) : PromiseOrValue < GraphQLResult < ReadonlyArray < unknown > > > {
1380+ // This is specified as a simple map, however we're optimizing the path
1381+ // where the list contains no Promises by avoiding creating another Promise.
1382+ let containsPromise = false ;
1383+ const completedResults : Array < unknown > = [ ] ;
1384+ const acc : GraphQLResult < Array < unknown > > = [ completedResults , [ ] ] ;
1385+ let index = 0 ;
1386+ const initialCount = streamUsage . initialCount ;
12211387 const iterator = items [ Symbol . iterator ] ( ) ;
12221388 let iteration = iterator . next ( ) ;
12231389 while ( ! iteration . done ) {
12241390 const item = iteration . value ;
12251391
1226- if ( streamUsage && index >= streamUsage . initialCount ) {
1392+ if ( index >= initialCount ) {
12271393 const streamRecord = new StreamRecord ( {
12281394 label : streamUsage . label ,
12291395 path,
0 commit comments