Skip to content

Commit 49986ed

Browse files
committed
stop resolvers after execution ends (graphql#4263)
depends on graphql#4267 addresses: graphql#3792
1 parent 2fab868 commit 49986ed

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/execution/__tests__/nonnull-test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
5+
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
56

67
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
78

@@ -526,6 +527,68 @@ describe('Execute: handles non-nullable types', () => {
526527
});
527528
});
528529

530+
describe('cancellation with null bubbling', () => {
531+
function nestedPromise(n: number): string {
532+
return n > 0 ? `promiseNest { ${nestedPromise(n - 1)} }` : 'promise';
533+
}
534+
535+
it('returns an single error without cancellation', async () => {
536+
const query = `
537+
{
538+
promiseNonNull,
539+
${nestedPromise(4)}
540+
}
541+
`;
542+
543+
const result = await executeQuery(query, throwingData);
544+
expectJSON(result).toDeepEqual({
545+
data: null,
546+
errors: [
547+
// does not include syncNullError because result returns prior to it being added
548+
{
549+
message: 'promiseNonNull',
550+
path: ['promiseNonNull'],
551+
locations: [{ line: 3, column: 11 }],
552+
},
553+
],
554+
});
555+
});
556+
557+
it('stops running despite error', async () => {
558+
const query = `
559+
{
560+
promiseNonNull,
561+
${nestedPromise(10)}
562+
}
563+
`;
564+
565+
let counter = 0;
566+
const rootValue = {
567+
...throwingData,
568+
promiseNest() {
569+
return new Promise((resolve) => {
570+
counter++;
571+
resolve(rootValue);
572+
});
573+
},
574+
};
575+
const result = await executeQuery(query, rootValue);
576+
expectJSON(result).toDeepEqual({
577+
data: null,
578+
errors: [
579+
{
580+
message: 'promiseNonNull',
581+
path: ['promiseNonNull'],
582+
locations: [{ line: 3, column: 11 }],
583+
},
584+
],
585+
});
586+
const counterAtExecutionEnd = counter;
587+
await resolveOnNextTick();
588+
expect(counter).to.equal(counterAtExecutionEnd);
589+
});
590+
});
591+
529592
describe('Handles non-null argument', () => {
530593
const schemaWithNonNullArg = new GraphQLSchema({
531594
query: new GraphQLObjectType({

src/execution/execute.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export interface ExecutionContext {
145145
validatedExecutionArgs: ValidatedExecutionArgs;
146146
errors: Array<GraphQLError>;
147147
promiseCanceller: PromiseCanceller | undefined;
148+
completed: boolean;
148149
}
149150

150151
/**
@@ -244,6 +245,7 @@ export function executeQueryOrMutationOrSubscriptionEvent(
244245
promiseCanceller: abortSignal
245246
? new PromiseCanceller(abortSignal)
246247
: undefined,
248+
completed: false,
247249
};
248250
try {
249251
const {
@@ -319,6 +321,7 @@ function buildResponse(
319321
exeContext: ExecutionContext,
320322
data: ObjMap<unknown> | null,
321323
): ExecutionResult {
324+
exeContext.completed = true;
322325
exeContext.promiseCanceller?.disconnect();
323326
const errors = exeContext.errors;
324327
return errors.length === 0 ? { data } : { errors, data };
@@ -712,6 +715,10 @@ function handleFieldError(
712715
fieldDetailsList: FieldDetailsList,
713716
path: Path,
714717
): void {
718+
if (exeContext.completed) {
719+
return;
720+
}
721+
715722
const error = locatedError(
716723
rawError,
717724
toNodes(fieldDetailsList),
@@ -1274,6 +1281,10 @@ function completeObjectValue(
12741281
path: Path,
12751282
result: unknown,
12761283
): PromiseOrValue<ObjMap<unknown>> {
1284+
if (exeContext.completed) {
1285+
throw new Error('Completed, aborting.');
1286+
}
1287+
12771288
// If there is an isTypeOf predicate function, call it with the
12781289
// current result. If isTypeOf returns false, then raise an error rather
12791290
// than continuing execution.

0 commit comments

Comments
 (0)