• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3// Modeled very closely on the AbortController implementation
4// in https://github.com/mysticatea/abort-controller (MIT license)
5
6const {
7  ObjectAssign,
8  ObjectDefineProperties,
9  ObjectSetPrototypeOf,
10  ObjectDefineProperty,
11  Symbol,
12  SymbolToStringTag,
13  TypeError,
14} = primordials;
15
16const {
17  defineEventHandler,
18  EventTarget,
19  Event,
20  kTrustEvent
21} = require('internal/event_target');
22const {
23  customInspectSymbol,
24  emitExperimentalWarning
25} = require('internal/util');
26const { inspect } = require('internal/util/inspect');
27const {
28  codes: {
29    ERR_INVALID_THIS,
30  }
31} = require('internal/errors');
32
33const kAborted = Symbol('kAborted');
34
35function customInspect(self, obj, depth, options) {
36  if (depth < 0)
37    return self;
38
39  const opts = ObjectAssign({}, options, {
40    depth: options.depth === null ? null : options.depth - 1
41  });
42
43  return `${self.constructor.name} ${inspect(obj, opts)}`;
44}
45
46function validateAbortSignal(obj) {
47  if (obj?.[kAborted] === undefined)
48    throw new ERR_INVALID_THIS('AbortSignal');
49}
50
51class AbortSignal extends EventTarget {
52  constructor() {
53    // eslint-disable-next-line no-restricted-syntax
54    throw new TypeError('Illegal constructor');
55  }
56
57  get aborted() {
58    validateAbortSignal(this);
59    return !!this[kAborted];
60  }
61
62  [customInspectSymbol](depth, options) {
63    return customInspect(this, {
64      aborted: this.aborted
65    }, depth, options);
66  }
67
68  static abort() {
69    return createAbortSignal(true);
70  }
71}
72
73ObjectDefineProperties(AbortSignal.prototype, {
74  aborted: { enumerable: true }
75});
76
77ObjectDefineProperty(AbortSignal.prototype, SymbolToStringTag, {
78  writable: false,
79  enumerable: false,
80  configurable: true,
81  value: 'AbortSignal',
82});
83
84defineEventHandler(AbortSignal.prototype, 'abort');
85
86function createAbortSignal(aborted = false) {
87  const signal = new EventTarget();
88  ObjectSetPrototypeOf(signal, AbortSignal.prototype);
89  signal[kAborted] = aborted;
90  return signal;
91}
92
93function abortSignal(signal) {
94  if (signal[kAborted]) return;
95  signal[kAborted] = true;
96  const event = new Event('abort', {
97    [kTrustEvent]: true
98  });
99  signal.dispatchEvent(event);
100}
101
102// TODO(joyeecheung): V8 snapshot does not support instance member
103// initializers for now:
104// https://bugs.chromium.org/p/v8/issues/detail?id=10704
105const kSignal = Symbol('signal');
106
107function validateAbortController(obj) {
108  if (obj?.[kSignal] === undefined)
109    throw new ERR_INVALID_THIS('AbortController');
110}
111
112class AbortController {
113  constructor() {
114    this[kSignal] = createAbortSignal();
115    emitExperimentalWarning('AbortController');
116  }
117
118  get signal() {
119    validateAbortController(this);
120    return this[kSignal];
121  }
122
123  abort() {
124    validateAbortController(this);
125    abortSignal(this[kSignal]);
126  }
127
128  [customInspectSymbol](depth, options) {
129    return customInspect(this, {
130      signal: this.signal
131    }, depth, options);
132  }
133}
134
135ObjectDefineProperties(AbortController.prototype, {
136  signal: { enumerable: true },
137  abort: { enumerable: true }
138});
139
140ObjectDefineProperty(AbortController.prototype, SymbolToStringTag, {
141  writable: false,
142  enumerable: false,
143  configurable: true,
144  value: 'AbortController',
145});
146
147module.exports = {
148  AbortController,
149  AbortSignal,
150};
151