1
1
import arrayFrom from '../polyfills/arrayFrom' ;
2
+ import { SYMBOL_ASYNC_ITERATOR } from '../polyfills/symbols' ;
2
3
3
4
import type { Path } from '../jsutils/Path' ;
4
5
import type { ObjMap } from '../jsutils/ObjMap' ;
@@ -8,6 +9,7 @@ import memoize3 from '../jsutils/memoize3';
8
9
import invariant from '../jsutils/invariant' ;
9
10
import devAssert from '../jsutils/devAssert' ;
10
11
import isPromise from '../jsutils/isPromise' ;
12
+ import isAsyncIterable from '../jsutils/isAsyncIterable' ;
11
13
import isObjectLike from '../jsutils/isObjectLike' ;
12
14
import isCollection from '../jsutils/isCollection' ;
13
15
import promiseReduce from '../jsutils/promiseReduce' ;
@@ -855,6 +857,74 @@ function completeValue(
855
857
) ;
856
858
}
857
859
860
+ /**
861
+ * Complete a async iterator value by completing the result and calling
862
+ * recursively until all the results are completed.
863
+ */
864
+ function completeAsyncIteratorValue (
865
+ exeContext : ExecutionContext ,
866
+ itemType : GraphQLOutputType ,
867
+ fieldNodes : $ReadOnlyArray < FieldNode > ,
868
+ info : GraphQLResolveInfo ,
869
+ path : Path ,
870
+ iterator : AsyncIterator < mixed > ,
871
+ ) : Promise < $ReadOnlyArray < mixed >> {
872
+ let containsPromise = false ;
873
+ return new Promise ( ( resolve ) => {
874
+ function next ( index , completedResults ) {
875
+ const fieldPath = addPath ( path , index , undefined ) ;
876
+ iterator . next ( ) . then (
877
+ ( { value, done } ) => {
878
+ if ( done ) {
879
+ resolve ( completedResults ) ;
880
+ return ;
881
+ }
882
+ // TODO can the error checking logic be consolidated with completeListValue?
883
+ try {
884
+ const completedItem = completeValue (
885
+ exeContext ,
886
+ itemType ,
887
+ fieldNodes ,
888
+ info ,
889
+ fieldPath ,
890
+ value ,
891
+ ) ;
892
+ if ( isPromise ( completedItem ) ) {
893
+ containsPromise = true ;
894
+ }
895
+ completedResults . push ( completedItem ) ;
896
+ } catch ( rawError ) {
897
+ completedResults . push ( null ) ;
898
+ const error = locatedError (
899
+ rawError ,
900
+ fieldNodes ,
901
+ pathToArray ( fieldPath ) ,
902
+ ) ;
903
+ handleFieldError ( error , itemType , exeContext ) ;
904
+ resolve ( completedResults ) ;
905
+ return ;
906
+ }
907
+
908
+ next ( index + 1 , completedResults ) ;
909
+ } ,
910
+ ( rawError ) => {
911
+ completedResults . push ( null ) ;
912
+ const error = locatedError (
913
+ rawError ,
914
+ fieldNodes ,
915
+ pathToArray ( fieldPath ) ,
916
+ ) ;
917
+ handleFieldError ( error , itemType , exeContext ) ;
918
+ resolve ( completedResults ) ;
919
+ } ,
920
+ ) ;
921
+ }
922
+ next ( 0 , [ ] ) ;
923
+ } ) . then ( ( completedResults ) =>
924
+ containsPromise ? Promise . all ( completedResults ) : completedResults ,
925
+ ) ;
926
+ }
927
+
858
928
/**
859
929
* Complete a list value by completing each item in the list with the
860
930
* inner type
@@ -867,6 +937,21 @@ function completeListValue(
867
937
path : Path ,
868
938
result : mixed ,
869
939
) : PromiseOrValue < $ReadOnlyArray < mixed >> {
940
+ const itemType = returnType . ofType ;
941
+
942
+ if ( isAsyncIterable ( result ) ) {
943
+ const iterator = result [ SYMBOL_ASYNC_ITERATOR ] ( ) ;
944
+
945
+ return completeAsyncIteratorValue (
946
+ exeContext ,
947
+ itemType ,
948
+ fieldNodes ,
949
+ info ,
950
+ path ,
951
+ iterator ,
952
+ ) ;
953
+ }
954
+
870
955
if ( ! isCollection ( result ) ) {
871
956
throw new GraphQLError (
872
957
`Expected Iterable, but did not find one for field "${ info . parentType . name } .${ info . fieldName } ".` ,
@@ -875,7 +960,6 @@ function completeListValue(
875
960
876
961
// This is specified as a simple map, however we're optimizing the path
877
962
// where the list contains no Promises by avoiding creating another Promise.
878
- const itemType = returnType . ofType ;
879
963
let containsPromise = false ;
880
964
const completedResults = arrayFrom ( result , ( item , index ) => {
881
965
// No need to modify the info object containing the path,
0 commit comments