Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.js1
-rw-r--r--doc/api/cli.md8
-rw-r--r--doc/api/globals.md96
-rw-r--r--doc/node.13
-rw-r--r--lib/internal/abort_controller.js83
-rw-r--r--lib/internal/bootstrap/pre_execution.js27
-rw-r--r--lib/internal/main/worker_thread.js2
-rw-r--r--node.gyp1
-rw-r--r--src/node_options.cc4
-rw-r--r--src/node_options.h1
-rw-r--r--test/common/index.js1
-rw-r--r--test/parallel/test-abortcontroller.js22
-rw-r--r--test/parallel/test-bootstrap-modules.js7
-rw-r--r--tools/doc/type-parser.js4
14 files changed, 260 insertions, 0 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index 8279dfc9c4a..8542d2e77e0 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -280,6 +280,7 @@ module.exports = {
'node-core/no-duplicate-requires': 'error',
},
globals: {
+ AbortController: 'readable',
Atomics: 'readable',
BigInt: 'readable',
BigInt64Array: 'readable',
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 46c2cad5e88..7e317c10c25 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -167,6 +167,13 @@ Enable experimental Source Map V3 support for stack traces.
Currently, overriding `Error.prepareStackTrace` is ignored when the
`--enable-source-maps` flag is set.
+### `--experimental-abortcontroller`
+<!-- YAML
+added: REPLACEME
+-->
+
+Enable experimental `AbortController` and `AbortSignal` support.
+
### `--experimental-import-meta-resolve`
<!-- YAML
added:
@@ -1209,6 +1216,7 @@ Node.js options that are allowed are:
* `--disable-proto`
* `--enable-fips`
* `--enable-source-maps`
+* `--experimental-abortcontroller`
* `--experimental-import-meta-resolve`
* `--experimental-json-modules`
* `--experimental-loader`
diff --git a/doc/api/globals.md b/doc/api/globals.md
index 7e6129e9038..78550035cbc 100644
--- a/doc/api/globals.md
+++ b/doc/api/globals.md
@@ -17,6 +17,101 @@ The objects listed here are specific to Node.js. There are [built-in objects][]
that are part of the JavaScript language itself, which are also globally
accessible.
+## Class: `AbortController`
+<!--YAML
+added: REPLACEME
+-->
+
+> Stability: 1 - Experimental
+
+<!-- type=global -->
+
+A utility class used to signal cancelation in selected `Promise`-based APIs.
+The API is based on the Web API [`AbortController`][].
+
+To use, launch Node.js using the `--experimental-abortcontroller` flag.
+
+```js
+const ac = new AbortController();
+
+ac.signal.addEventListener('abort', () => console.log('Aborted!'),
+ { once: true });
+
+ac.abort();
+
+console.log(ac.signal.aborted); // Prints True
+```
+
+### `abortController.abort()`
+<!-- YAML
+added: REPLACEME
+-->
+
+Triggers the abort signal, causing the `abortController.signal` to emit
+the `'abort'` event.
+
+### `abortController.signal`
+<!-- YAML
+added: REPLACEME
+-->
+
+* Type: {AbortSignal}
+
+### Class: `AbortSignal extends EventTarget`
+<!-- YAML
+added: REPLACEME
+-->
+
+The `AbortSignal` is used to notify observers when the
+`abortController.abort()` method is called.
+
+#### Event: `'abort'`
+<!-- YAML
+added: REPLACEME
+-->
+
+The `'abort'` event is emitted when the `abortController.abort()` method
+is called. The callback is invoked with a single object argument with a
+single `type` propety set to `'abort'`:
+
+```js
+const ac = new AbortController();
+
+// Use either the onabort property...
+ac.signal.onabort = () => console.log('aborted!');
+
+// Or the EventTarget API...
+ac.signal.addEventListener('abort', (event) => {
+ console.log(event.type); // Prints 'abort'
+}, { once: true });
+
+ac.abort();
+```
+
+The `AbortController` with which the `AbortSignal` is associated will only
+ever trigger the `'abort'` event once. Any event listeners attached to the
+`AbortSignal` *should* use the `{ once: true }` option (or, if using the
+`EventEmitter` APIs to attach a listener, use the `once()` method) to ensure
+that the event listener is removed as soon as the `'abort'` event is handled.
+Failure to do so may result in memory leaks.
+
+#### `abortSignal.aborted`
+<!-- YAML
+added: REPLACEME
+-->
+
+* Type: {boolean} True after the `AbortController` has been aborted.
+
+#### `abortSignal.onabort`
+<!-- YAML
+added: REPLACEME
+-->
+
+* Type: {Function}
+
+An optional callback function that may be set by user code to be notified
+when the `abortController.abort()` function has been called.
+
## Class: `Buffer`
<!-- YAML
added: v0.1.103
@@ -226,6 +321,7 @@ The object that acts as the namespace for all W3C
[WebAssembly][webassembly-org] related functionality. See the
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
+[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
[`TextDecoder`]: util.html#util_class_util_textdecoder
[`TextEncoder`]: util.html#util_class_util_textencoder
[`URLSearchParams`]: url.html#url_class_urlsearchparams
diff --git a/doc/node.1 b/doc/node.1
index 1a217a8bcf9..272b53d4892 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -118,6 +118,9 @@ Enable FIPS-compliant crypto at startup.
Requires Node.js to be built with
.Sy ./configure --openssl-fips .
.
+.It Fl -experimental-abortcontroller
+Enable experimental AbortController and AbortSignal support.
+.
.It Fl -enable-source-maps
Enable experimental Source Map V3 support for stack traces.
.
diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js
new file mode 100644
index 00000000000..b4ba6dd4f87
--- /dev/null
+++ b/lib/internal/abort_controller.js
@@ -0,0 +1,83 @@
+'use strict';
+
+// Modeled very closely on the AbortController implementation
+// in https://github.com/mysticatea/abort-controller (MIT license)
+
+const {
+ Object,
+ Symbol,
+} = primordials;
+
+const {
+ EventTarget,
+ Event
+} = require('internal/event_target');
+const {
+ customInspectSymbol,
+ emitExperimentalWarning
+} = require('internal/util');
+const { inspect } = require('internal/util/inspect');
+
+const kAborted = Symbol('kAborted');
+
+function customInspect(self, obj, depth, options) {
+ if (depth < 0)
+ return self;
+
+ const opts = Object.assign({}, options, {
+ depth: options.depth === null ? null : options.depth - 1
+ });
+
+ return `${self.constructor.name} ${inspect(obj, opts)}`;
+}
+
+class AbortSignal extends EventTarget {
+ get aborted() { return !!this[kAborted]; }
+
+ [customInspectSymbol](depth, options) {
+ return customInspect(this, {
+ aborted: this.aborted
+ }, depth, options);
+ }
+}
+
+Object.defineProperties(AbortSignal.prototype, {
+ aborted: { enumerable: true }
+});
+
+function abortSignal(signal) {
+ if (signal[kAborted]) return;
+ signal[kAborted] = true;
+ const event = new Event('abort');
+ if (typeof signal.onabort === 'function') {
+ signal.onabort(event);
+ }
+ signal.dispatchEvent(event);
+}
+
+class AbortController {
+ #signal = new AbortSignal();
+
+ constructor() {
+ emitExperimentalWarning('AbortController');
+ }
+
+ get signal() { return this.#signal; }
+ abort() { abortSignal(this.#signal); }
+
+ [customInspectSymbol](depth, options) {
+ return customInspect(this, {
+ signal: this.signal
+ }, depth, options);
+ }
+}
+
+Object.defineProperties(AbortController.prototype, {
+ signal: { enumerable: true },
+ abort: { enumerable: true }
+});
+
+module.exports = {
+ AbortController,
+ AbortSignal,
+};
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js
index f60814d2dc9..9a0b2c361d3 100644
--- a/lib/internal/bootstrap/pre_execution.js
+++ b/lib/internal/bootstrap/pre_execution.js
@@ -3,6 +3,7 @@
const {
Map,
ObjectDefineProperty,
+ ObjectDefineProperties,
SafeWeakMap,
} = primordials;
@@ -53,6 +54,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
// (including preload modules).
initializeClusterIPC();
+ initializeAbortController();
initializeDeprecations();
initializeWASI();
initializeCJSLoader();
@@ -304,6 +306,30 @@ function initializeDeprecations() {
});
}
+function initializeAbortController() {
+ const abortController = getOptionValue('--experimental-abortcontroller');
+ if (abortController) {
+ const {
+ AbortController,
+ AbortSignal
+ } = require('internal/abort_controller');
+ ObjectDefineProperties(global, {
+ AbortController: {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+ value: AbortController
+ },
+ AbortSignal: {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+ value: AbortSignal
+ }
+ });
+ }
+}
+
function setupChildProcessIpcChannel() {
if (process.env.NODE_CHANNEL_FD) {
const assert = require('internal/assert');
@@ -438,6 +464,7 @@ module.exports = {
setupWarningHandler,
setupDebugEnv,
prepareMainThreadExecution,
+ initializeAbortController,
initializeDeprecations,
initializeESMLoader,
initializeFrozenIntrinsics,
diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js
index 61045cf1c08..31c23993e80 100644
--- a/lib/internal/main/worker_thread.js
+++ b/lib/internal/main/worker_thread.js
@@ -13,6 +13,7 @@ const {
setupInspectorHooks,
setupWarningHandler,
setupDebugEnv,
+ initializeAbortController,
initializeDeprecations,
initializeWASI,
initializeCJSLoader,
@@ -112,6 +113,7 @@ port.on('message', (message) => {
if (manifestSrc) {
require('internal/process/policy').setup(manifestSrc, manifestURL);
}
+ initializeAbortController();
initializeDeprecations();
initializeWASI();
initializeCJSLoader();
diff --git a/node.gyp b/node.gyp
index a5b95f428d4..36cab71f563 100644
--- a/node.gyp
+++ b/node.gyp
@@ -95,6 +95,7 @@
'lib/wasi.js',
'lib/worker_threads.js',
'lib/zlib.js',
+ 'lib/internal/abort_controller.js',
'lib/internal/assert.js',
'lib/internal/assert/assertion_error.js',
'lib/internal/assert/calltracker.js',
diff --git a/src/node_options.cc b/src/node_options.cc
index cb812f6ccfc..68bd8757cc7 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -274,6 +274,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"experimental Source Map V3 support",
&EnvironmentOptions::enable_source_maps,
kAllowedInEnvironment);
+ AddOption("--experimental-abortcontroller",
+ "experimental AbortController support",
+ &EnvironmentOptions::experimental_abortcontroller,
+ kAllowedInEnvironment);
AddOption("--experimental-json-modules",
"experimental JSON interop support for the ES Module loader",
&EnvironmentOptions::experimental_json_modules,
diff --git a/src/node_options.h b/src/node_options.h
index 54710e48770..b908e639951 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -101,6 +101,7 @@ class EnvironmentOptions : public Options {
public:
bool abort_on_uncaught_exception = false;
bool enable_source_maps = false;
+ bool experimental_abortcontroller = false;
bool experimental_json_modules = false;
bool experimental_modules = false;
std::string experimental_specifier_resolution;
diff --git a/test/common/index.js b/test/common/index.js
index f7a29f5d2f4..d3645629a7e 100644
--- a/test/common/index.js
+++ b/test/common/index.js
@@ -256,6 +256,7 @@ function platformTimeout(ms) {
}
let knownGlobals = [
+ AbortController,
clearImmediate,
clearInterval,
clearTimeout,
diff --git a/test/parallel/test-abortcontroller.js b/test/parallel/test-abortcontroller.js
new file mode 100644
index 00000000000..c1e28e16146
--- /dev/null
+++ b/test/parallel/test-abortcontroller.js
@@ -0,0 +1,22 @@
+// Flags: --no-warnings --experimental-abortcontroller
+'use strict';
+
+const common = require('../common');
+
+const { ok, strictEqual } = require('assert');
+
+{
+ const ac = new AbortController();
+ ok(ac.signal);
+ ac.signal.onabort = common.mustCall((event) => {
+ ok(event);
+ strictEqual(event.type, 'abort');
+ });
+ ac.signal.addEventListener('abort', common.mustCall((event) => {
+ ok(event);
+ strictEqual(event.type, 'abort');
+ }), { once: true });
+ ac.abort();
+ ac.abort();
+ ok(ac.signal.aborted);
+}
diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js
index 62775b95279..b2506499e7d 100644
--- a/test/parallel/test-bootstrap-modules.js
+++ b/test/parallel/test-bootstrap-modules.js
@@ -20,6 +20,7 @@ const expectedModules = new Set([
'Internal Binding module_wrap',
'Internal Binding native_module',
'Internal Binding options',
+ 'Internal Binding performance',
'Internal Binding process_methods',
'Internal Binding report',
'Internal Binding string_decoder',
@@ -30,9 +31,11 @@ const expectedModules = new Set([
'Internal Binding types',
'Internal Binding url',
'Internal Binding util',
+ 'NativeModule async_hooks',
'NativeModule buffer',
'NativeModule events',
'NativeModule fs',
+ 'NativeModule internal/abort_controller',
'NativeModule internal/assert',
'NativeModule internal/async_hooks',
'NativeModule internal/bootstrap/pre_execution',
@@ -42,9 +45,11 @@ const expectedModules = new Set([
'NativeModule internal/constants',
'NativeModule internal/encoding',
'NativeModule internal/errors',
+ 'NativeModule internal/event_target',
'NativeModule internal/fixed_queue',
'NativeModule internal/fs/dir',
'NativeModule internal/fs/utils',
+ 'NativeModule internal/histogram',
'NativeModule internal/idna',
'NativeModule internal/linkedlist',
'NativeModule internal/modules/run_main',
@@ -81,8 +86,10 @@ const expectedModules = new Set([
'NativeModule internal/validators',
'NativeModule internal/vm/module',
'NativeModule path',
+ 'NativeModule perf_hooks',
'NativeModule timers',
'NativeModule url',
+ 'NativeModule util',
'NativeModule vm',
]);
diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js
index ec6c8e54ef8..0a5a795011a 100644
--- a/tools/doc/type-parser.js
+++ b/tools/doc/type-parser.js
@@ -26,6 +26,10 @@ const customTypesMap = {
'this': `${jsDocPrefix}Reference/Operators/this`,
+ 'AbortController': 'globals.html#globals_class_abortcontroller',
+ 'AbortSignal':
+ 'globals.html#globals_class_abortsignal_extends_eventtarget',
+
'ArrayBufferView':
'https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView',