diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 686e3267504..d9f61b8e7c0 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -748,15 +748,33 @@ function executeField( const result = resolveFn(source, args, contextValue, info); if (isPromise(result)) { - return completePromisedValue( - exeContext, - returnType, - fieldGroup, - info, - path, - result, - incrementalContext, - deferMap, + return ( + result + .then((resolved) => + completeValue( + exeContext, + returnType, + fieldGroup, + info, + path, + resolved, + incrementalContext, + deferMap, + ), + ) + // Note: we don't rely on a `catch` method, but we do expect "thenable" + // to take a second callback for the error case. + .then(undefined, (rawError) => { + handleFieldError( + rawError, + exeContext, + returnType, + fieldGroup, + path, + incrementalContext, + ); + return null; + }) ); } @@ -972,46 +990,6 @@ function completeValue( ); } -async function completePromisedValue( - exeContext: ExecutionContext, - returnType: GraphQLOutputType, - fieldGroup: FieldGroup, - info: GraphQLResolveInfo, - path: Path, - result: Promise, - incrementalContext: IncrementalContext | undefined, - deferMap: ReadonlyMap | undefined, -): Promise { - try { - const resolved = await result; - let completed = completeValue( - exeContext, - returnType, - fieldGroup, - info, - path, - resolved, - incrementalContext, - deferMap, - ); - - if (isPromise(completed)) { - completed = await completed; - } - return completed; - } catch (rawError) { - handleFieldError( - rawError, - exeContext, - returnType, - fieldGroup, - path, - incrementalContext, - ); - return null; - } -} - /** * Returns an object containing info for streaming if a field should be * streamed based on the experimental flag, stream directive present and @@ -1452,16 +1430,32 @@ function completeListItemValue( ): boolean { if (isPromise(item)) { completedResults.push( - completePromisedValue( - exeContext, - itemType, - fieldGroup, - info, - itemPath, - item, - incrementalContext, - deferMap, - ), + item + .then((resolved) => + completeValue( + exeContext, + itemType, + fieldGroup, + info, + itemPath, + resolved, + incrementalContext, + deferMap, + ), + ) + // Note: we don't rely on a `catch` method, but we do expect "thenable" + // to take a second callback for the error case. + .then(undefined, (rawError) => { + handleFieldError( + rawError, + exeContext, + itemType, + fieldGroup, + itemPath, + incrementalContext, + ); + return null; + }), ); return true; } @@ -2488,24 +2482,43 @@ function completeStreamItems( itemType: GraphQLOutputType, ): PromiseOrValue { if (isPromise(item)) { - return completePromisedValue( - exeContext, - itemType, - fieldGroup, - info, - itemPath, - item, - incrementalContext, - new Map(), - ).then( - (resolvedItem) => - buildStreamItemsResult(incrementalContext, streamRecord, resolvedItem), - (error) => ({ - streamRecord, - result: null, - errors: withError(incrementalContext.errors, error), - }), - ); + return item + .then((resolved) => + completeValue( + exeContext, + itemType, + fieldGroup, + info, + itemPath, + resolved, + incrementalContext, + new Map(), + ), + ) + .then(undefined, (rawError) => { + handleFieldError( + rawError, + exeContext, + itemType, + fieldGroup, + itemPath, + incrementalContext, + ); + return null; + }) + .then( + (resolvedItem) => + buildStreamItemsResult( + incrementalContext, + streamRecord, + resolvedItem, + ), + (error) => ({ + streamRecord, + result: null, + errors: withError(incrementalContext.errors, error), + }), + ); } let completedItem;