Skip to content

Commit a3c3978

Browse files
authored
fix(cancellablePromise): handle rejection when cancelling already-aborted promise (#4645)
1 parent 41a671c commit a3c3978

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

src/execution/__tests__/cancellablePromise-test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { describe, it } from 'mocha';
33

44
import { expectPromise } from '../../__testUtils__/expectPromise.js';
55

6+
import { promiseWithResolvers } from '../../jsutils/promiseWithResolvers.js';
7+
68
import { cancellablePromise } from '../cancellablePromise.js';
79

810
describe('cancellablePromise', () => {
@@ -110,4 +112,34 @@ describe('cancellablePromise', () => {
110112

111113
await expectPromise(withCancellation).toRejectWith('Cancelled!');
112114
});
115+
116+
it('handles later original rejections when already aborted', async () => {
117+
const abortController = new AbortController();
118+
abortController.abort(new Error('Cancelled!'));
119+
120+
const deferred = promiseWithResolvers<undefined>();
121+
122+
let unhandledRejection: unknown = null;
123+
const unhandledRejectionListener = (reason: unknown) => {
124+
unhandledRejection = reason;
125+
};
126+
// eslint-disable-next-line no-undef
127+
process.on('unhandledRejection', unhandledRejectionListener);
128+
129+
try {
130+
const withCancellation = cancellablePromise(
131+
deferred.promise,
132+
abortController.signal,
133+
);
134+
await expectPromise(withCancellation).toRejectWith('Cancelled!');
135+
136+
deferred.reject(new Error('Rejected later'));
137+
await new Promise((resolve) => setTimeout(resolve, 20));
138+
} finally {
139+
// eslint-disable-next-line no-undef
140+
process.removeListener('unhandledRejection', unhandledRejectionListener);
141+
}
142+
143+
expect(unhandledRejection).to.equal(null);
144+
});
113145
});

src/execution/cancellablePromise.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ export function cancellablePromise<T>(
55
abortSignal: AbortSignal,
66
): Promise<T> {
77
if (abortSignal.aborted) {
8+
// If cancellation has already happened, still drain the original promise to
9+
// avoid unhandled rejections from work that settles later.
10+
originalPromise.catch(() => undefined);
811
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
912
return Promise.reject(abortSignal.reason);
1013
}

0 commit comments

Comments
 (0)