@@ -386,8 +386,6 @@ function executeOperation(
386
386
// Errors from sub-fields of a NonNull type may propagate to the top level,
387
387
// at which point we still log the error and null the parent field, which
388
388
// in this case is the entire response.
389
- //
390
- // Similar to completeValueCatchingError.
391
389
try {
392
390
const result =
393
391
operation . operation === 'mutation'
@@ -643,6 +641,7 @@ function resolveField(
643
641
return;
644
642
}
645
643
644
+ const returnType = fieldDef . type ;
646
645
const resolveFn = fieldDef . resolve ?? exeContext . fieldResolver ;
647
646
648
647
const info = buildResolveInfo (
@@ -653,67 +652,7 @@ function resolveField(
653
652
path ,
654
653
) ;
655
654
656
- // Get the resolve function, regardless of if its result is normal
657
- // or abrupt (error).
658
- const result = resolveFieldValueOrError (
659
- exeContext ,
660
- fieldDef ,
661
- fieldNodes ,
662
- resolveFn ,
663
- source ,
664
- info ,
665
- ) ;
666
-
667
- return completeValueCatchingError (
668
- exeContext ,
669
- fieldDef . type ,
670
- fieldNodes ,
671
- info ,
672
- path ,
673
- result ,
674
- ) ;
675
- }
676
-
677
- /**
678
- * @internal
679
- */
680
- export function buildResolveInfo(
681
- exeContext: ExecutionContext,
682
- fieldDef: GraphQLField< mixed , mixed > ,
683
- fieldNodes: $ReadOnlyArray< FieldNode > ,
684
- parentType: GraphQLObjectType,
685
- path: Path,
686
- ): GraphQLResolveInfo {
687
- // The resolve function's optional fourth argument is a collection of
688
- // information about the current execution state.
689
- return {
690
- fieldName : fieldDef . name ,
691
- fieldNodes,
692
- returnType : fieldDef . type ,
693
- parentType,
694
- path,
695
- schema : exeContext . schema ,
696
- fragments : exeContext . fragments ,
697
- rootValue : exeContext . rootValue ,
698
- operation : exeContext . operation ,
699
- variableValues : exeContext . variableValues ,
700
- } ;
701
- }
702
-
703
- /**
704
- * Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField`
705
- * function. Returns the result of resolveFn or the abrupt-return Error object.
706
- *
707
- * @internal
708
- */
709
- export function resolveFieldValueOrError(
710
- exeContext: ExecutionContext,
711
- fieldDef: GraphQLField< mixed , mixed > ,
712
- fieldNodes: $ReadOnlyArray< FieldNode > ,
713
- resolveFn: GraphQLFieldResolver< mixed , mixed > ,
714
- source: mixed,
715
- info: GraphQLResolveInfo,
716
- ): Error | mixed {
655
+ // Get the resolve function, regardless of if its result is normal or abrupt (error).
717
656
try {
718
657
// Build a JS object of arguments from the field.arguments AST, using the
719
658
// variables scope to fulfill any variable references.
@@ -730,32 +669,7 @@ export function resolveFieldValueOrError(
730
669
const contextValue = exeContext . contextValue ;
731
670
732
671
const result = resolveFn ( source , args , contextValue , info ) ;
733
- return isPromise ( result ) ? result . then ( undefined , asErrorInstance ) : result ;
734
- } catch ( error ) {
735
- return asErrorInstance ( error ) ;
736
- }
737
- }
738
-
739
- // Sometimes a non-error is thrown, wrap it as an Error instance to ensure a
740
- // consistent Error interface.
741
- function asErrorInstance(error: mixed): Error {
742
- if ( error instanceof Error ) {
743
- return error ;
744
- }
745
- return new Error('Unexpected error value: ' + inspect(error));
746
- }
747
672
748
- // This is a small wrapper around completeValue which detects and logs errors
749
- // in the execution context.
750
- function completeValueCatchingError (
751
- exeContext : ExecutionContext ,
752
- returnType : GraphQLOutputType ,
753
- fieldNodes : $ReadOnlyArray < FieldNode > ,
754
- info: GraphQLResolveInfo,
755
- path: Path,
756
- result: mixed,
757
- ): PromiseOrValue< mixed > {
758
- try {
759
673
let completed ;
760
674
if ( isPromise ( result ) ) {
761
675
completed = result . then ( ( resolved ) =>
@@ -785,12 +699,34 @@ function completeValueCatchingError(
785
699
}
786
700
}
787
701
788
- function handleFieldError ( rawError , fieldNodes , path , returnType , exeContext ) {
789
- const error = locatedError (
790
- asErrorInstance ( rawError ) ,
702
+ /**
703
+ * @internal
704
+ */
705
+ export function buildResolveInfo (
706
+ exeContext : ExecutionContext ,
707
+ fieldDef : GraphQLField < mixed , mixed > ,
708
+ fieldNodes : $ReadOnlyArray < FieldNode > ,
709
+ parentType : GraphQLObjectType ,
710
+ path : Path ,
711
+ ) : GraphQLResolveInfo {
712
+ // The resolve function's optional fourth argument is a collection of
713
+ // information about the current execution state.
714
+ return {
715
+ fieldName : fieldDef . name ,
791
716
fieldNodes ,
792
- pathToArray ( path ) ,
793
- ) ;
717
+ returnType : fieldDef . type ,
718
+ parentType ,
719
+ path ,
720
+ schema : exeContext . schema ,
721
+ fragments : exeContext . fragments ,
722
+ rootValue : exeContext . rootValue ,
723
+ operation : exeContext . operation ,
724
+ variableValues : exeContext . variableValues ,
725
+ } ;
726
+ }
727
+
728
+ function handleFieldError ( rawError , fieldNodes , path , returnType , exeContext ) {
729
+ const error = locatedError ( rawError , fieldNodes , pathToArray ( path ) ) ;
794
730
795
731
// If the field type is non-nullable, then it is resolved without any
796
732
// protection from errors, however it still properly locates the error.
@@ -939,21 +875,49 @@ function completeListValue(
939
875
const completedResults = arrayFrom ( result , ( item , index ) => {
940
876
// No need to modify the info object containing the path,
941
877
// since from here on it is not ever accessed by resolver functions.
942
- const fieldPath = addPath ( path , index , undefined ) ;
943
- const completedItem = completeValueCatchingError (
944
- exeContext ,
945
- itemType ,
946
- fieldNodes ,
947
- info ,
948
- fieldPath ,
949
- item ,
950
- ) ;
878
+ const itemPath = addPath ( path , index , undefined ) ;
879
+ try {
880
+ let completedItem ;
881
+ if ( isPromise ( item ) ) {
882
+ completedItem = item . then ( ( resolved ) =>
883
+ completeValue (
884
+ exeContext ,
885
+ itemType ,
886
+ fieldNodes ,
887
+ info ,
888
+ itemPath ,
889
+ resolved ,
890
+ ) ,
891
+ ) ;
892
+ } else {
893
+ completedItem = completeValue (
894
+ exeContext ,
895
+ itemType ,
896
+ fieldNodes ,
897
+ info ,
898
+ itemPath ,
899
+ item ,
900
+ ) ;
901
+ }
951
902
952
- if ( isPromise ( completedItem ) ) {
953
- containsPromise = true ;
903
+ if ( isPromise ( completedItem ) ) {
904
+ containsPromise = true ;
905
+ // Note: we don't rely on a `catch` method, but we do expect "thenable"
906
+ // to take a second callback for the error case.
907
+ return completedItem . then ( undefined , ( error ) =>
908
+ handleFieldError ( error , fieldNodes , itemPath , itemType , exeContext ) ,
909
+ ) ;
910
+ }
911
+ return completedItem ;
912
+ } catch ( error ) {
913
+ return handleFieldError (
914
+ error ,
915
+ fieldNodes ,
916
+ itemPath ,
917
+ itemType ,
918
+ exeContext ,
919
+ ) ;
954
920
}
955
-
956
- return completedItem;
957
921
} ) ;
958
922
959
923
return containsPromise ? Promise . all ( completedResults ) : completedResults ;
0 commit comments