Skip to content

Commit d03118a

Browse files
authored
fix: bubbling sync errors need not become async (#4661)
1 parent 229c9df commit d03118a

File tree

5 files changed

+66
-32
lines changed

5 files changed

+66
-32
lines changed

src/execution/Executor.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,7 @@ export class Executor<
509509
} catch (error) {
510510
if (containsPromise) {
511511
// Ensure that any promises returned by other fields are handled, as they may also reject.
512-
return promiseForObject(results).finally(() => {
513-
throw error;
514-
}) as never;
512+
promiseForObject(results).catch(() => undefined);
515513
}
516514
throw error;
517515
}
@@ -860,9 +858,7 @@ export class Executor<
860858
// eslint-disable-next-line @typescript-eslint/no-floating-promises
861859
returnIteratorCatchingErrors(asyncIterator);
862860
if (containsPromise) {
863-
return Promise.all(completedResults).finally(() => {
864-
throw error;
865-
});
861+
Promise.all(completedResults).catch(() => undefined);
866862
}
867863
throw error;
868864
}
@@ -1000,9 +996,7 @@ export class Executor<
1000996
const maybePromises = containsPromise ? completedResults : [];
1001997
maybePromises.push(...collectIteratorPromises(iterator));
1002998
if (maybePromises.length) {
1003-
return Promise.all(maybePromises).finally(() => {
1004-
throw error;
1005-
});
999+
Promise.all(maybePromises).catch(() => undefined);
10061000
}
10071001
throw error;
10081002
}

src/execution/__tests__/cancellation-test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,31 @@ describe('Execute: Cancellation', () => {
667667
).to.throw('This operation was aborted');
668668
});
669669

670+
it('should stop the execution when aborted before cancellation is wired', async () => {
671+
const abortController = new AbortController();
672+
const document = parse(`
673+
query {
674+
blocker
675+
}
676+
`);
677+
678+
const resultPromise = execute({
679+
document,
680+
schema,
681+
abortSignal: abortController.signal,
682+
rootValue: {
683+
blocker: () => {
684+
abortController.abort(new Error('Custom abort error'));
685+
return new Promise(() => {
686+
/* will never resolve */
687+
});
688+
},
689+
},
690+
});
691+
692+
await expectPromise(resultPromise).toRejectWith('Custom abort error');
693+
});
694+
670695
it('should stop the execution when aborted prior to return of a subscription resolver', async () => {
671696
const abortController = new AbortController();
672697
const document = parse(`

src/execution/__tests__/executor-test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,6 @@ describe('Execute: Handles basic execution tasks', () => {
680680

681681
const result = execute({ schema, document });
682682

683-
expect(isAsyncResolverFinished).to.equal(false);
684683
expectJSON(await result).toDeepEqual({
685684
data: null,
686685
errors: [
@@ -692,6 +691,11 @@ describe('Execute: Handles basic execution tasks', () => {
692691
},
693692
],
694693
});
694+
695+
expect(isAsyncResolverFinished).to.equal(false);
696+
await resolveOnNextTick();
697+
await resolveOnNextTick();
698+
await resolveOnNextTick();
695699
expect(isAsyncResolverFinished).to.equal(true);
696700
});
697701

src/execution/__tests__/incremental-test.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,33 +167,44 @@ describe('Original execute errors on experimental @defer and @stream directives'
167167
query: new GraphQLObjectType({
168168
name: 'Query',
169169
fields: {
170-
scalarList: { type: new GraphQLList(GraphQLString) },
171-
friendList: {
172-
type: new GraphQLList(
173-
new GraphQLObjectType({
174-
name: 'Friend',
175-
fields: {
176-
name: { type: GraphQLString },
170+
nested: {
171+
type: new GraphQLObjectType({
172+
name: 'Nested',
173+
fields: {
174+
scalarList: { type: new GraphQLList(GraphQLString) },
175+
friendList: {
176+
type: new GraphQLList(
177+
new GraphQLObjectType({
178+
name: 'Friend',
179+
fields: {
180+
name: { type: GraphQLString },
181+
},
182+
}),
183+
),
177184
},
178-
}),
179-
),
185+
},
186+
}),
180187
},
181188
},
182189
}),
183190
});
184191
const doc = `
185192
query {
186-
scalarList
187-
friendList @stream { name }
193+
nested {
194+
scalarList
195+
friendList @stream { name }
196+
}
188197
}
189198
`;
190199
await expectPromise(
191200
execute({
192201
schema,
193202
document: parse(doc),
194203
rootValue: {
195-
scalarList: Promise.resolve(['apple', 'banana', 'coconut']),
196-
friendList: [{ name: 'Alice' }, { name: 'Bob' }],
204+
nested: Promise.resolve({
205+
scalarList: Promise.resolve(['apple', 'banana', 'coconut']),
206+
friendList: [{ name: 'Alice' }, { name: 'Bob' }],
207+
}),
197208
},
198209
}),
199210
).toRejectWith(

src/execution/legacyIncremental/__tests__/legacy-defer-test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,15 +1710,6 @@ describe('Execute: defer directive (legacy)', () => {
17101710
},
17111711
hasNext: true,
17121712
},
1713-
{
1714-
incremental: [
1715-
{
1716-
data: { b: { e: { f: 'f' } }, someField: 'someField' },
1717-
path: ['a'],
1718-
},
1719-
],
1720-
hasNext: true,
1721-
},
17221713
{
17231714
incremental: [
17241715
{
@@ -1734,6 +1725,15 @@ describe('Execute: defer directive (legacy)', () => {
17341725
],
17351726
},
17361727
],
1728+
hasNext: true,
1729+
},
1730+
{
1731+
incremental: [
1732+
{
1733+
data: { b: { e: { f: 'f' } }, someField: 'someField' },
1734+
path: ['a'],
1735+
},
1736+
],
17371737
hasNext: false,
17381738
},
17391739
]);

0 commit comments

Comments
 (0)