import { getGlobalAbortSignalListenerManager } from "./abort-signal-listener-manager.ts" /** * @description 表示支持的中止感知输入。 */ export type Abortable = AbortSignal | AbortController | AbortManager /** * @description 表示可能包含 `abortSignal` 的选项对象。 */ export interface WithAbortSignal { abortSignal?: AbortSignal | undefined } /** * @description 从选项类型中移除 `abortSignal` 字段。 */ export type WithoutAbortSignal = Omit /** * @description 从中止感知输入中解析出 `AbortSignal`。 */ export const getAbortSignal = (abortable: Abortable): AbortSignal => { if (abortable instanceof AbortSignal) { return abortable } else if (abortable instanceof AbortController) { return abortable.signal } else if (abortable instanceof AbortManager) { return abortable.abortSignal } else { throw new TypeError("Unsupported abortable type.") } } /** * @description 检查中止感知输入是否已经中止。 */ export const isAborted = (abortable: Abortable): boolean => { const signal = getAbortSignal(abortable) return signal.aborted } /** * @description 表示暴露 `AbortManager` 与 `abort` 操作的对象。 */ export interface AbortFriendly { readonly abortManager: AbortManager /** * @description 此方法中应该将 {@link abortManager} 中止。 */ abort: (reason: string) => void } /** * @description 表示可提供上游 `AbortManager` 的选项。 */ export interface AbortFriendlyOptions { abortManager?: AbortManager | undefined } /** * @description 定义支持的中止判定模式。 */ export const ABORT_MANAGER_MODE = { ALL: "all", ANY: "any", } as const /** * @description 表示 `AbortManager` 使用的中止模式。 */ export type AbortManagerMode = (typeof ABORT_MANAGER_MODE)[keyof typeof ABORT_MANAGER_MODE] interface Upstream { abortable: Abortable listener: () => void connect: () => void disconnect: () => void remove: () => void } /** * @description 表示 `AbortManager` 的构造选项。 */ export interface AbortManagerOptions extends AbortFriendlyOptions { /** * @description 如果为 true,则在中止后移除全部上游。 * * @default true */ autoRemoveUpstreamsAfterAbort?: boolean | undefined } /** * @description 协调一个或多个上游中止源的下游中止状态。 */ export class AbortManager implements AbortFriendly { readonly abortManager: AbortManager readonly abortController: AbortController readonly abortSignal: AbortSignal protected upstreamMap: Map protected mode: AbortManagerMode protected autoRemoveUpstreamsAfterAbort: boolean constructor(options: AbortManagerOptions) { this.abortManager = this this.abortController = new AbortController() this.abortSignal = this.abortController.signal this.upstreamMap = new Map() this.mode = ABORT_MANAGER_MODE.ALL this.autoRemoveUpstreamsAfterAbort = options.autoRemoveUpstreamsAfterAbort ?? true const abortManagerFromOptions = options.abortManager if (abortManagerFromOptions !== undefined) { this.addUpstreams([abortManagerFromOptions]) } } /** * @description 检查当前管理器是否已经中止。 */ isAborted(): boolean { return this.abortSignal.aborted } /** * @description 设置中止判定模式。 */ setMode(mode: AbortManagerMode): this { this.mode = mode return this } /** * @description 添加需要观察的上游中止源。 */ addUpstreams(abortables: Abortable[]): this { const globalAbortSignalListenerManager = getGlobalAbortSignalListenerManager() abortables.forEach((abortable) => { const abortSignal = getAbortSignal(abortable) const listener = (): void => { this.tryToAbort() // 任一上游中止后都应重新尝试判定是否中止 } const connect = (): void => { globalAbortSignalListenerManager.addEventListener(abortSignal, listener) } const disconnect = (): void => { globalAbortSignalListenerManager.removeEventListener(abortSignal, listener) } const remove = (): void => { disconnect() this.upstreamMap.delete(abortable) } this.upstreamMap.set(abortable, { abortable, listener, connect, disconnect, remove, }) connect() }) // 添加上游后,立即重新判定是否需要中止 this.tryToAbort() return this } /** * @description 移除指定的上游中止源。 */ removeUpstreams(abortables: Abortable[]): this { abortables.forEach((abortable) => { const upstream = this.upstreamMap.get(abortable) if (upstream !== undefined) { upstream.remove() } }) // 移除上游后,立即重新判定是否需要中止 this.tryToAbort() return this } /** * @description 移除全部已注册的上游中止源。 */ removeAllUpstreams(): this { const upstreamAbortables = Array.from(this.upstreamMap.keys()) this.removeUpstreams(upstreamAbortables) return this } /** * @description 根据当前模式评估上游状态,并在满足条件时中止。 */ tryToAbort(): void { const upstreams = Array.from(this.upstreamMap.values()) let shouldAbort: boolean if (this.mode === ABORT_MANAGER_MODE.ALL) { shouldAbort = upstreams.every((upstream) => isAborted(upstream.abortable)) } else if (this.mode === ABORT_MANAGER_MODE.ANY) { shouldAbort = upstreams.some((upstream) => isAborted(upstream.abortable)) } else { throw new TypeError("Unsupported mode.") } // 满足中止条件且当前尚未中止时,执行中止 if (shouldAbort === true && this.abortSignal.aborted === false) { this.abort(`Abort by upstream, mode: ${this.mode}.`) } } /** * @description 使用给定原因中止当前管理器。 */ abort(reason: string): void { this.abortController.abort(reason) if (this.autoRemoveUpstreamsAfterAbort === true) { this.removeAllUpstreams() } } } /** * @description 基于单个上游中止源创建 `AbortManager`。 */ export const fromAbortable = (abortable: Abortable): AbortManager => { const abortManager = new AbortManager({}) abortManager.addUpstreams([abortable]) return abortManager } /** * @description 创建在任一上游中止时中止的 `AbortManager`。 */ export const any = (abortables: Array): AbortManager => { const abortManager = new AbortManager({}) abortManager.setMode(ABORT_MANAGER_MODE.ANY) abortManager.addUpstreams(abortables.filter((item) => item !== undefined && item !== null)) return abortManager } /** * @description 创建在全部上游中止时中止的 `AbortManager`。 */ export const all = (abortables: Array): AbortManager => { const abortManager = new AbortManager({}) abortManager.setMode(ABORT_MANAGER_MODE.ALL) abortManager.addUpstreams(abortables.filter((item) => item !== undefined && item !== null)) return abortManager } /** * @description 从可能携带 `abortSignal` 的选项对象创建 `AbortManager`。 */ export const fromWithAbortSignal = (options: T): AbortManager => { const { abortSignal } = options const abortManager = new AbortManager({}) if (abortSignal !== undefined) { abortManager.addUpstreams([abortSignal]) } return abortManager } /** * @description 从中止友好的选项对象创建 `AbortManager`。 */ export const fromAbortFriendlyOptions = ( options: T, ): AbortManager => { const abortManager = new AbortManager({ ...options }) return abortManager }