mirror of
https://github.com/denoland/deno.git
synced 2025-01-08 15:19:40 -05:00
Add support for AbortController/AbortSignal (#4757)
This commit is contained in:
parent
95eb6d780c
commit
cb64cf3ce2
6 changed files with 154 additions and 4 deletions
|
@ -2,6 +2,8 @@
|
|||
|
||||
import "./lib.deno.shared_globals.d.ts";
|
||||
|
||||
import * as abortController from "./web/abort_controller.ts";
|
||||
import * as abortSignal from "./web/abort_signal.ts";
|
||||
import * as blob from "./web/blob.ts";
|
||||
import * as consoleTypes from "./web/console.ts";
|
||||
import * as promiseTypes from "./web/promise.ts";
|
||||
|
@ -207,6 +209,8 @@ export const windowOrWorkerGlobalScopeMethods = {
|
|||
// Other properties shared between WindowScope and WorkerGlobalScope
|
||||
export const windowOrWorkerGlobalScopeProperties = {
|
||||
console: writable(new consoleTypes.Console(core.print)),
|
||||
AbortController: nonEnumerable(abortController.AbortControllerImpl),
|
||||
AbortSignal: nonEnumerable(abortSignal.AbortSignalImpl),
|
||||
Blob: nonEnumerable(blob.DenoBlob),
|
||||
File: nonEnumerable(domFile.DomFileImpl),
|
||||
CustomEvent: nonEnumerable(customEvent.CustomEventImpl),
|
||||
|
|
16
cli/js/lib.deno.shared_globals.d.ts
vendored
16
cli/js/lib.deno.shared_globals.d.ts
vendored
|
@ -1256,6 +1256,16 @@ declare class CustomEvent<T = any> extends Event {
|
|||
readonly detail: T;
|
||||
}
|
||||
|
||||
/** A controller object that allows you to abort one or more DOM requests as and
|
||||
* when desired. */
|
||||
declare class AbortController {
|
||||
/** Returns the AbortSignal object associated with this object. */
|
||||
readonly signal: AbortSignal;
|
||||
/** Invoking this method will set this object's AbortSignal's aborted flag and
|
||||
* signal to any observers that the associated activity is to be aborted. */
|
||||
abort(): void;
|
||||
}
|
||||
|
||||
interface AbortSignalEventMap {
|
||||
abort: Event;
|
||||
}
|
||||
|
@ -1263,10 +1273,8 @@ interface AbortSignalEventMap {
|
|||
/** A signal object that allows you to communicate with a DOM request (such as a
|
||||
* Fetch) and abort it if required via an AbortController object. */
|
||||
interface AbortSignal extends EventTarget {
|
||||
/**
|
||||
* Returns true if this AbortSignal's AbortController has signaled to abort,
|
||||
* and false otherwise.
|
||||
*/
|
||||
/** Returns true if this AbortSignal's AbortController has signaled to abort,
|
||||
* and false otherwise. */
|
||||
readonly aborted: boolean;
|
||||
onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
||||
addEventListener<K extends keyof AbortSignalEventMap>(
|
||||
|
|
56
cli/js/tests/abort_controller_test.ts
Normal file
56
cli/js/tests/abort_controller_test.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { unitTest, assert, assertEquals } from "./test_util.ts";
|
||||
|
||||
unitTest(function basicAbortController() {
|
||||
const controller = new AbortController();
|
||||
assert(controller);
|
||||
const { signal } = controller;
|
||||
assert(signal);
|
||||
assertEquals(signal.aborted, false);
|
||||
controller.abort();
|
||||
assertEquals(signal.aborted, true);
|
||||
});
|
||||
|
||||
unitTest(function signalCallsOnabort() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.onabort = (evt): void => {
|
||||
assert(evt);
|
||||
assertEquals(evt.type, "abort");
|
||||
called = true;
|
||||
};
|
||||
controller.abort();
|
||||
assert(called);
|
||||
});
|
||||
|
||||
unitTest(function signalEventListener() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.addEventListener("abort", function (ev) {
|
||||
assert(this === signal);
|
||||
assertEquals(ev.type, "abort");
|
||||
called = true;
|
||||
});
|
||||
controller.abort();
|
||||
assert(called);
|
||||
});
|
||||
|
||||
unitTest(function onlyAbortsOnce() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = 0;
|
||||
signal.addEventListener("abort", () => called++);
|
||||
signal.onabort = (): void => {
|
||||
called++;
|
||||
};
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
});
|
||||
|
||||
unitTest(function controllerHasProperToString() {
|
||||
const actual = Object.prototype.toString.call(new AbortController());
|
||||
assertEquals(actual, "[object AbortController]");
|
||||
});
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Test runner automatically spawns subprocesses for each required permissions combination.
|
||||
|
||||
import "./abort_controller_test.ts";
|
||||
import "./blob_test.ts";
|
||||
import "./body_test.ts";
|
||||
import "./buffer_test.ts";
|
||||
|
|
23
cli/js/web/abort_controller.ts
Normal file
23
cli/js/web/abort_controller.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { AbortSignalImpl, signalAbort } from "./abort_signal.ts";
|
||||
|
||||
export class AbortControllerImpl implements AbortController {
|
||||
#signal = new AbortSignalImpl();
|
||||
|
||||
get signal(): AbortSignal {
|
||||
return this.#signal;
|
||||
}
|
||||
|
||||
abort(): void {
|
||||
this.#signal[signalAbort]();
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "AbortController";
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(AbortControllerImpl, "name", {
|
||||
value: "AbortController",
|
||||
configurable: true,
|
||||
});
|
58
cli/js/web/abort_signal.ts
Normal file
58
cli/js/web/abort_signal.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { EventImpl } from "./event.ts";
|
||||
import { EventTargetImpl } from "./event_target.ts";
|
||||
|
||||
export const add = Symbol("add");
|
||||
export const signalAbort = Symbol("signalAbort");
|
||||
export const remove = Symbol("remove");
|
||||
|
||||
export class AbortSignalImpl extends EventTargetImpl implements AbortSignal {
|
||||
#aborted?: boolean;
|
||||
#abortAlgorithms = new Set<() => void>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onabort: ((this: AbortSignal, ev: Event) => any) | null = null;
|
||||
|
||||
[add](algorithm: () => void): void {
|
||||
this.#abortAlgorithms.add(algorithm);
|
||||
}
|
||||
|
||||
[signalAbort](): void {
|
||||
if (this.#aborted) {
|
||||
return;
|
||||
}
|
||||
this.#aborted = true;
|
||||
for (const algorithm of this.#abortAlgorithms) {
|
||||
algorithm();
|
||||
}
|
||||
this.#abortAlgorithms.clear();
|
||||
this.dispatchEvent(new EventImpl("abort"));
|
||||
}
|
||||
|
||||
[remove](algorithm: () => void): void {
|
||||
this.#abortAlgorithms.delete(algorithm);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener("abort", (evt: Event) => {
|
||||
const { onabort } = this;
|
||||
if (typeof onabort === "function") {
|
||||
onabort.call(this, evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get aborted(): boolean {
|
||||
return Boolean(this.#aborted);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "AbortSignal";
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(AbortSignalImpl, "name", {
|
||||
value: "AbortSignal",
|
||||
configurable: true,
|
||||
});
|
Loading…
Reference in a new issue