feat: add AbortSignal support (#8672)

* feat: add `AbortSignal` support

* fix: move the expect earlier

* fix: pass signal

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
A. Román
2022-09-25 20:44:03 +02:00
committed by GitHub
parent 9f63eb977f
commit 3c231ae81a
3 changed files with 51 additions and 4 deletions

View File

@@ -93,6 +93,10 @@ export interface RequestData {
* Reason to show in the audit logs
*/
reason?: string;
/**
* The signal to abort the queue entry or the REST call, where applicable
*/
signal?: AbortSignal | undefined;
/**
* If this request should be versioned
*
@@ -133,7 +137,7 @@ export interface InternalRequest extends RequestData {
method: RequestMethod;
}
export type HandlerRequestData = Pick<InternalRequest, 'auth' | 'body' | 'files'>;
export type HandlerRequestData = Pick<InternalRequest, 'auth' | 'body' | 'files' | 'signal'>;
/**
* Parsed route data for an endpoint
@@ -338,6 +342,7 @@ export class RequestManager extends EventEmitter {
body: request.body,
files: request.files,
auth: request.auth !== false,
signal: request.signal,
});
}

View File

@@ -175,7 +175,7 @@ export class SequentialHandler implements IHandler {
}
// Wait for any previous requests to be completed before this one is run
await queue.wait();
await queue.wait({ signal: requestData.signal });
// This set handles retroactively sublimiting requests
if (queueType === QueueType.Standard) {
if (this.#sublimitedQueue && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
@@ -293,8 +293,17 @@ export class SequentialHandler implements IHandler {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.manager.options.timeout).unref();
let res: Dispatcher.ResponseData;
if (requestData.signal) {
// The type polyfill is required because Node.js's types are incomplete.
const signal = requestData.signal as PolyFillAbortSignal;
// If the user signal was aborted, abort the controller, else abort the local signal.
// The reason why we don't re-use the user's signal, is because users may use the same signal for multiple
// requests, and we do not want to cause unexpected side-effects.
if (signal.aborted) controller.abort();
else signal.addEventListener('abort', () => controller.abort());
}
let res: Dispatcher.ResponseData;
try {
res = await request(url, { ...options, signal: controller.signal });
} catch (error: unknown) {
@@ -492,3 +501,9 @@ export class SequentialHandler implements IHandler {
}
}
}
interface PolyFillAbortSignal {
readonly aborted: boolean;
addEventListener(type: 'abort', listener: () => void): void;
removeEventListener(type: 'abort', listener: () => void): void;
}