1
0
Fork 0
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:
Kitson Kelly 2020-04-16 00:10:49 +10:00 committed by GitHub
parent 95eb6d780c
commit cb64cf3ce2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 4 deletions

View file

@ -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),

View file

@ -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>(

View 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]");
});

View file

@ -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";

View 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,
});

View 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,
});