@@ -715,25 +715,15 @@ function executeField(
715
715
const result = resolveFn ( source , args , contextValue , info ) ;
716
716
717
717
if ( isPromise ( result ) ) {
718
- const completed = result . then ( ( resolved ) =>
719
- completeValue (
720
- exeContext ,
721
- returnType ,
722
- fieldNodes ,
723
- info ,
724
- path ,
725
- resolved ,
726
- asyncPayloadRecord ,
727
- ) ,
718
+ return completePromisedValue (
719
+ exeContext ,
720
+ returnType ,
721
+ fieldNodes ,
722
+ info ,
723
+ path ,
724
+ result ,
725
+ asyncPayloadRecord ,
728
726
) ;
729
- // Note: we don't rely on a `catch` method, but we do expect "thenable"
730
- // to take a second callback for the error case.
731
- return completed . then ( undefined , ( rawError ) => {
732
- const error = locatedError ( rawError , fieldNodes , pathToArray ( path ) ) ;
733
- const handledError = handleFieldError ( error , returnType , errors ) ;
734
- filterSubsequentPayloads ( exeContext , path , asyncPayloadRecord ) ;
735
- return handledError ;
736
- } ) ;
737
727
}
738
728
739
729
const completed = completeValue (
@@ -922,6 +912,41 @@ function completeValue(
922
912
) ;
923
913
}
924
914
915
+ async function completePromisedValue (
916
+ exeContext : ExecutionContext ,
917
+ returnType : GraphQLOutputType ,
918
+ fieldNodes : ReadonlyArray < FieldNode > ,
919
+ info : GraphQLResolveInfo ,
920
+ path : Path ,
921
+ result : Promise < unknown > ,
922
+ asyncPayloadRecord ?: AsyncPayloadRecord ,
923
+ ) : Promise < unknown > {
924
+ try {
925
+ const resolved = await result ;
926
+ let completed = completeValue (
927
+ exeContext ,
928
+ returnType ,
929
+ fieldNodes ,
930
+ info ,
931
+ path ,
932
+ resolved ,
933
+ asyncPayloadRecord ,
934
+ ) ;
935
+ if ( isPromise ( completed ) ) {
936
+ // see: https://github.com/tc39/proposal-faster-promise-adoption
937
+ // it is faster to await a promise prior to returning it from an async function
938
+ completed = await completed ;
939
+ }
940
+ return completed ;
941
+ } catch ( rawError ) {
942
+ const errors = asyncPayloadRecord ?. errors ?? exeContext . errors ;
943
+ const error = locatedError ( rawError , fieldNodes , pathToArray ( path ) ) ;
944
+ const handledError = handleFieldError ( error , returnType , errors ) ;
945
+ filterSubsequentPayloads ( exeContext , path , asyncPayloadRecord ) ;
946
+ return handledError ;
947
+ }
948
+ }
949
+
925
950
/**
926
951
* Returns an object containing the `@stream` arguments if a field should be
927
952
* streamed based on the experimental flag, stream directive present and
@@ -1156,29 +1181,18 @@ function completeListItemValue(
1156
1181
asyncPayloadRecord ?: AsyncPayloadRecord ,
1157
1182
) : boolean {
1158
1183
if ( isPromise ( item ) ) {
1159
- const completedItem = item . then ( ( resolved ) =>
1160
- completeValue (
1184
+ completedResults . push (
1185
+ completePromisedValue (
1161
1186
exeContext ,
1162
1187
itemType ,
1163
1188
fieldNodes ,
1164
1189
info ,
1165
1190
itemPath ,
1166
- resolved ,
1191
+ item ,
1167
1192
asyncPayloadRecord ,
1168
1193
) ,
1169
1194
) ;
1170
1195
1171
- // Note: we don't rely on a `catch` method, but we do expect "thenable"
1172
- // to take a second callback for the error case.
1173
- completedResults . push (
1174
- completedItem . then ( undefined , ( rawError ) => {
1175
- const error = locatedError ( rawError , fieldNodes , pathToArray ( itemPath ) ) ;
1176
- const handledError = handleFieldError ( error , itemType , errors ) ;
1177
- filterSubsequentPayloads ( exeContext , itemPath , asyncPayloadRecord ) ;
1178
- return handledError ;
1179
- } ) ,
1180
- ) ;
1181
-
1182
1196
return true ;
1183
1197
}
1184
1198
@@ -1897,36 +1911,22 @@ function executeStreamField(
1897
1911
exeContext,
1898
1912
} ) ;
1899
1913
if ( isPromise ( item ) ) {
1900
- const completedItems = item
1901
- . then ( ( resolved ) =>
1902
- completeValue (
1903
- exeContext ,
1904
- itemType ,
1905
- fieldNodes ,
1906
- info ,
1907
- itemPath ,
1908
- resolved ,
1909
- asyncPayloadRecord ,
1910
- ) ,
1911
- )
1912
- . then ( undefined , ( rawError ) => {
1913
- const error = locatedError ( rawError , fieldNodes , pathToArray ( itemPath ) ) ;
1914
- const handledError = handleFieldError (
1915
- error ,
1916
- itemType ,
1917
- asyncPayloadRecord . errors ,
1918
- ) ;
1919
- filterSubsequentPayloads ( exeContext , itemPath , asyncPayloadRecord ) ;
1920
- return handledError ;
1921
- } )
1922
- . then (
1923
- ( value ) => [ value ] ,
1924
- ( error ) => {
1925
- asyncPayloadRecord . errors . push ( error ) ;
1926
- filterSubsequentPayloads ( exeContext , path , asyncPayloadRecord ) ;
1927
- return null ;
1928
- } ,
1929
- ) ;
1914
+ const completedItems = completePromisedValue (
1915
+ exeContext ,
1916
+ itemType ,
1917
+ fieldNodes ,
1918
+ info ,
1919
+ itemPath ,
1920
+ item ,
1921
+ asyncPayloadRecord ,
1922
+ ) . then (
1923
+ ( value ) => [ value ] ,
1924
+ ( error ) => {
1925
+ asyncPayloadRecord . errors . push ( error ) ;
1926
+ filterSubsequentPayloads ( exeContext , path , asyncPayloadRecord ) ;
1927
+ return null ;
1928
+ } ,
1929
+ ) ;
1930
1930
1931
1931
asyncPayloadRecord . addItems ( completedItems ) ;
1932
1932
return asyncPayloadRecord ;
0 commit comments