UNPKG

3.98 kBJavaScriptView Raw
1class Emitter {
2 constructor() {
3 Object.defineProperty(this, 'listeners', { value: {}, writable: true, configurable: true });
4 }
5 addEventListener(type, callback, options) {
6 if (!(type in this.listeners)) {
7 this.listeners[type] = [];
8 }
9 this.listeners[type].push({ callback, options });
10 }
11 removeEventListener(type, callback) {
12 if (!(type in this.listeners)) {
13 return;
14 }
15 const stack = this.listeners[type];
16 for (let i = 0, l = stack.length; i < l; i++) {
17 if (stack[i].callback === callback) {
18 stack.splice(i, 1);
19 return;
20 }
21 }
22 }
23 dispatchEvent(event) {
24 if (!(event.type in this.listeners)) {
25 return;
26 }
27 const stack = this.listeners[event.type];
28 const stackToCall = stack.slice();
29 for (let i = 0, l = stackToCall.length; i < l; i++) {
30 const listener = stackToCall[i];
31 try {
32 listener.callback.call(this, event);
33 } catch (e) {
34 Promise.resolve().then(() => {
35 throw e;
36 });
37 }
38 if (listener.options && listener.options.once) {
39 this.removeEventListener(event.type, listener.callback);
40 }
41 }
42 return !event.defaultPrevented;
43 }
44}
45
46export class AbortSignal extends Emitter {
47 constructor() {
48 super();
49 // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
50 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
51 // the parent constructor directly instead as a workaround. For general details, see babel bug:
52 // https://github.com/babel/babel/issues/3041
53 // This hack was added as a fix for the issue described here:
54 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
55 if (!this.listeners) {
56 Emitter.call(this);
57 }
58
59 // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
60 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
61 Object.defineProperty(this, 'aborted', { value: false, writable: true, configurable: true });
62 Object.defineProperty(this, 'onabort', { value: null, writable: true, configurable: true });
63 }
64 toString() {
65 return '[object AbortSignal]';
66 }
67 dispatchEvent(event) {
68 if (event.type === 'abort') {
69 this.aborted = true;
70 if (typeof this.onabort === 'function') {
71 this.onabort.call(this, event);
72 }
73 }
74
75 super.dispatchEvent(event);
76 }
77}
78
79export class AbortController {
80 constructor() {
81 // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
82 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
83 Object.defineProperty(this, 'signal', { value: new AbortSignal(), writable: true, configurable: true });
84 }
85 abort() {
86 let event;
87 try {
88 event = new Event('abort');
89 } catch (e) {
90 if (typeof document !== 'undefined') {
91 if (!document.createEvent) {
92 // For Internet Explorer 8:
93 event = document.createEventObject();
94 event.type = 'abort';
95 } else {
96 // For Internet Explorer 11:
97 event = document.createEvent('Event');
98 event.initEvent('abort', false, false);
99 }
100 } else {
101 // Fallback where document isn't available:
102 event = {
103 type: 'abort',
104 bubbles: false,
105 cancelable: false,
106 };
107 }
108 }
109 this.signal.dispatchEvent(event);
110 }
111 toString() {
112 return '[object AbortController]';
113 }
114}
115
116export default AbortController;
117
118if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
119 // These are necessary to make sure that we get correct output for:
120 // Object.prototype.toString.call(new AbortController())
121 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
122 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
123}