feat: AbortController polyfill (#9333)

This commit is contained in:
farfromrefuge
2021-08-11 20:17:13 +02:00
committed by Nathan Walker
parent c4db847ded
commit 58442fb454
3 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,80 @@
import { Observable } from '../data/observable';
// Known Limitation
// Use `any` because the type of `AbortSignal` in `lib.dom.d.ts` is wrong and
// to make assignable our `AbortSignal` into that.
// https://github.com/Microsoft/TSJS-lib-generator/pull/623
type Events = {
abort: any // Event & Type<"abort">
}
type EventAttributes = {
onabort: any // Event & Type<"abort">
}
/**
* The signal class.
* @see https://dom.spec.whatwg.org/#abortsignal
*/
export default class AbortSignal extends Observable {
/**
* AbortSignal cannot be constructed directly.
*/
public constructor() {
super()
}
/**
* Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.
*/
public get aborted(): boolean {
const aborted = abortedFlags.get(this)
if (typeof aborted !== "boolean") {
throw new TypeError(
`Expected 'this' to be an 'AbortSignal' object, but got ${
this === null ? "null" : typeof this
}`,
)
}
return aborted
}
}
/**
* Create an AbortSignal object.
*/
export function createAbortSignal(): AbortSignal {
const signal = new AbortSignal();
abortedFlags.set(signal, false)
return signal
}
/**
* Abort a given signal.
*/
export function abortSignal(signal: AbortSignal): void {
if (abortedFlags.get(signal) !== false) {
return
}
abortedFlags.set(signal, true)
signal.notify({ eventName: "abort", type: "abort" })
}
/**
* Aborted flag for each instances.
*/
const abortedFlags = new WeakMap<AbortSignal, boolean>()
// Properties should be enumerable.
Object.defineProperties(AbortSignal.prototype, {
aborted: { enumerable: true },
})
// `toString()` should return `"[object AbortSignal]"`
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {
configurable: true,
value: "AbortSignal",
})
}

View File

@ -0,0 +1,62 @@
import AbortSignal, { abortSignal, createAbortSignal } from "./abortsignal"
/**
* The AbortController.
* @see https://dom.spec.whatwg.org/#abortcontroller
*/
export default class AbortController {
/**
* Initialize this controller.
*/
public constructor() {
signals.set(this, createAbortSignal())
}
/**
* Returns the `AbortSignal` object associated with this object.
*/
public get signal(): AbortSignal {
return getSignal(this)
}
/**
* Abort and signal to any observers that the associated activity is to be aborted.
*/
public abort(): void {
abortSignal(getSignal(this))
}
}
/**
* Associated signals.
*/
const signals = new WeakMap<AbortController, AbortSignal>()
/**
* Get the associated signal of a given controller.
*/
function getSignal(controller: AbortController): AbortSignal {
const signal = signals.get(controller)
if (signal == null) {
throw new TypeError(
`Expected 'this' to be an 'AbortController' object, but got ${
controller === null ? "null" : typeof controller
}`,
)
}
return signal
}
// Properties should be enumerable.
Object.defineProperties(AbortController.prototype, {
signal: { enumerable: true },
abort: { enumerable: true },
})
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
Object.defineProperty(AbortController.prototype, Symbol.toStringTag, {
configurable: true,
value: "AbortController",
})
}
export { AbortController, AbortSignal }

View File

@ -319,6 +319,9 @@ export function initGlobal() {
global.registerModule('fetch', () => require('../fetch'));
installPolyfills('fetch', ['fetch', 'Headers', 'Request', 'Response']);
global.registerModule('abortcontroller', () => require('../abortcontroller'));
installPolyfills('abortcontroller', ['AbortController', 'AbortSignal']);
// Custom decorators
global.Deprecated = function (target: Object, key?: string | symbol, descriptor?: any) {