@@ -199,33 +199,35 @@ export interface FormattedExecutionResult<
199199export class Executor {
200200 validatedExecutionArgs : ValidatedExecutionArgs ;
201201 finished : boolean ;
202- resolverAbortControllers : Map < Path , AbortController > ;
203202 collectedErrors : CollectedErrors ;
203+ internalAbortController : AbortController ;
204+ resolverAbortController : AbortController | undefined ;
205+ sharedResolverAbortSignal : AbortSignal ;
204206
205207 constructor ( validatedExecutionArgs : ValidatedExecutionArgs ) {
206208 this . validatedExecutionArgs = validatedExecutionArgs ;
207209 this . finished = false ;
208- this . resolverAbortControllers = new Map ( ) ;
209210 this . collectedErrors = new CollectedErrors ( ) ;
211+ this . internalAbortController = new AbortController ( ) ;
212+
213+ this . resolverAbortController = new AbortController ( ) ;
214+ this . sharedResolverAbortSignal = this . resolverAbortController . signal ;
210215 }
211216
212217 executeQueryOrMutationOrSubscriptionEvent ( ) : PromiseOrValue < ExecutionResult > {
213- const validatedExecutionArgs = this . validatedExecutionArgs ;
214- const externalAbortSignal = validatedExecutionArgs . externalAbortSignal ;
215- let removeAbortListener : ( ( ) => void ) | undefined ;
218+ const externalAbortSignal = this . validatedExecutionArgs . externalAbortSignal ;
219+ let removeExternalAbortListener : ( ( ) => void ) | undefined ;
216220 if ( externalAbortSignal ) {
217- if ( externalAbortSignal . aborted ) {
218- throw new Error ( externalAbortSignal . reason ) ;
219- }
221+ externalAbortSignal . throwIfAborted ( ) ;
220222 const onExternalAbort = ( ) => this . cancel ( externalAbortSignal . reason ) ;
221- removeAbortListener = ( ) =>
223+ removeExternalAbortListener = ( ) =>
222224 externalAbortSignal . removeEventListener ( 'abort' , onExternalAbort ) ;
223225 externalAbortSignal . addEventListener ( 'abort' , onExternalAbort ) ;
224226 }
225227
226228 const onFinish = ( ) => {
227- removeAbortListener ?.( ) ;
228229 this . finish ( ) ;
230+ removeExternalAbortListener ?.( ) ;
229231 } ;
230232
231233 try {
@@ -236,7 +238,7 @@ export class Executor {
236238 operation,
237239 variableValues,
238240 hideSuggestions,
239- } = validatedExecutionArgs ;
241+ } = this . validatedExecutionArgs ;
240242
241243 const { operation : operationType , selectionSet } = operation ;
242244
@@ -276,59 +278,38 @@ export class Executor {
276278 return this . buildResponse ( null ) ;
277279 } ,
278280 ) ;
279- return externalAbortSignal
280- ? cancellablePromise ( promise , externalAbortSignal )
281- : promise ;
281+ return cancellablePromise ( promise , this . internalAbortController . signal ) ;
282282 }
283283 onFinish ( ) ;
284284 return this . buildResponse ( result ) ;
285285 } catch ( error ) {
286- this . collectedErrors . add ( error as GraphQLError , undefined ) ;
287286 onFinish ( ) ;
287+ this . collectedErrors . add ( error as GraphQLError , undefined ) ;
288288 return this . buildResponse ( null ) ;
289289 }
290290 }
291291
292292 cancel ( reason ?: unknown ) : void {
293293 if ( ! this . finished ) {
294- this . finish ( reason ) ;
294+ this . finish ( ) ;
295+ this . internalAbortController . abort ( reason ) ;
296+ this . resolverAbortController ?. abort ( reason ) ;
295297 }
296298 }
297299
298- finish ( reason ?: unknown ) : void {
300+ finish ( ) : void {
299301 if ( ! this . finished ) {
300302 this . finished = true ;
301- this . triggerResolverAbortSignals ( reason ) ;
302303 }
303- }
304-
305- triggerResolverAbortSignals ( reason ?: unknown ) : void {
306- const { resolverAbortControllers } = this ;
307- const finishReason =
308- reason ?? new Error ( 'Execution has already completed.' ) ;
309- for ( const abortController of resolverAbortControllers . values ( ) ) {
310- abortController . abort ( finishReason ) ;
311- }
312- }
313-
314- getAbortSignal ( path : Path ) : AbortSignal {
315- const resolverAbortSignal = this . resolverAbortControllers . get ( path ) ?. signal ;
316- if ( resolverAbortSignal !== undefined ) {
317- return resolverAbortSignal ;
318- }
319- const abortController = new AbortController ( ) ;
320- this . resolverAbortControllers . set ( path , abortController ) ;
321- if ( this . finished ) {
322- abortController . abort ( new Error ( 'Execution has already completed.' ) ) ;
323- }
324- return abortController . signal ;
304+ this . internalAbortController . signal . throwIfAborted ( ) ;
325305 }
326306
327307 /**
328308 * Given a completed execution context and data, build the `{ errors, data }`
329309 * response defined by the "Response" section of the GraphQL specification.
330310 */
331311 buildResponse ( data : ObjMap < unknown > | null ) : ExecutionResult {
312+ this . resolverAbortController ?. abort ( ) ;
332313 const errors = this . collectedErrors . errors ;
333314 return errors . length ? { errors, data } : { data } ;
334315 }
@@ -489,7 +470,7 @@ export class Executor {
489470 toNodes ( fieldDetailsList ) ,
490471 parentType ,
491472 path ,
492- ( ) => this . getAbortSignal ( path ) ,
473+ ( ) => this . sharedResolverAbortSignal ,
493474 ) ;
494475
495476 // Get the resolve function, regardless of if its result is normal or abrupt (error).
@@ -517,7 +498,6 @@ export class Executor {
517498 info ,
518499 path ,
519500 result ,
520- true ,
521501 ) ;
522502 }
523503
@@ -532,22 +512,13 @@ export class Executor {
532512 if ( isPromise ( completed ) ) {
533513 // Note: we don't rely on a `catch` method, but we do expect "thenable"
534514 // to take a second callback for the error case.
535- return completed . then (
536- ( resolved ) => {
537- this . resolverAbortControllers . delete ( path ) ;
538- return resolved ;
539- } ,
540- ( rawError : unknown ) => {
541- this . resolverAbortControllers . delete ( path ) ;
542- this . handleFieldError ( rawError , returnType , fieldDetailsList , path ) ;
543- return null ;
544- } ,
545- ) ;
515+ return completed . then ( undefined , ( rawError : unknown ) => {
516+ this . handleFieldError ( rawError , returnType , fieldDetailsList , path ) ;
517+ return null ;
518+ } ) ;
546519 }
547- this . resolverAbortControllers . delete ( path ) ;
548520 return completed ;
549521 } catch ( rawError ) {
550- this . resolverAbortControllers . delete ( path ) ;
551522 this . handleFieldError ( rawError , returnType , fieldDetailsList , path ) ;
552523 return null ;
553524 }
@@ -694,7 +665,6 @@ export class Executor {
694665 info : GraphQLResolveInfo ,
695666 path : Path ,
696667 result : Promise < unknown > ,
697- isFieldValue ?: boolean ,
698668 ) : Promise < unknown > {
699669 try {
700670 const resolved = await result ;
@@ -711,14 +681,8 @@ export class Executor {
711681 if ( isPromise ( completed ) ) {
712682 completed = await completed ;
713683 }
714- if ( isFieldValue ) {
715- this . resolverAbortControllers . delete ( path ) ;
716- }
717684 return completed ;
718685 } catch ( rawError ) {
719- if ( isFieldValue ) {
720- this . resolverAbortControllers . delete ( path ) ;
721- }
722686 this . handleFieldError ( rawError , returnType , fieldDetailsList , path ) ;
723687 return null ;
724688 }
0 commit comments