@@ -715,25 +715,15 @@ function executeField(
715715 const result = resolveFn ( source , args , contextValue , info ) ;
716716
717717 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 ,
728726 ) ;
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- } ) ;
737727 }
738728
739729 const completed = completeValue (
@@ -922,6 +912,41 @@ function completeValue(
922912 ) ;
923913}
924914
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+
925950/**
926951 * Returns an object containing the `@stream` arguments if a field should be
927952 * streamed based on the experimental flag, stream directive present and
@@ -1156,29 +1181,18 @@ function completeListItemValue(
11561181 asyncPayloadRecord ?: AsyncPayloadRecord ,
11571182) : boolean {
11581183 if ( isPromise ( item ) ) {
1159- const completedItem = item . then ( ( resolved ) =>
1160- completeValue (
1184+ completedResults . push (
1185+ completePromisedValue (
11611186 exeContext ,
11621187 itemType ,
11631188 fieldNodes ,
11641189 info ,
11651190 itemPath ,
1166- resolved ,
1191+ item ,
11671192 asyncPayloadRecord ,
11681193 ) ,
11691194 ) ;
11701195
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-
11821196 return true ;
11831197 }
11841198
@@ -1897,36 +1911,22 @@ function executeStreamField(
18971911 exeContext,
18981912 } ) ;
18991913 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+ ) ;
19301930
19311931 asyncPayloadRecord . addItems ( completedItems ) ;
19321932 return asyncPayloadRecord ;
0 commit comments