UNPKG

3.61 kBJavaScriptView Raw
1/**
2 * Observable class prototype.
3 *
4 * @module observable
5 */
6
7import * as map from './map.js'
8import * as set from './set.js'
9import * as array from './array.js'
10
11/**
12 * Handles named events.
13 * @experimental
14 *
15 * This is basically a (better typed) duplicate of Observable, which will replace Observable in the
16 * next release.
17 *
18 * @template {{[key: string]: function(...any):void}} EVENTS
19 */
20export class ObservableV2 {
21 constructor () {
22 /**
23 * Some desc.
24 * @type {Map<string, Set<any>>}
25 */
26 this._observers = map.create()
27 }
28
29 /**
30 * @template {string} NAME
31 * @param {NAME} name
32 * @param {EVENTS[NAME]} f
33 */
34 on (name, f) {
35 map.setIfUndefined(this._observers, /** @type {string} */ (name), set.create).add(f)
36 return f
37 }
38
39 /**
40 * @template {string} NAME
41 * @param {NAME} name
42 * @param {EVENTS[NAME]} f
43 */
44 once (name, f) {
45 /**
46 * @param {...any} args
47 */
48 const _f = (...args) => {
49 this.off(name, /** @type {any} */ (_f))
50 f(...args)
51 }
52 this.on(name, /** @type {any} */ (_f))
53 }
54
55 /**
56 * @template {string} NAME
57 * @param {NAME} name
58 * @param {EVENTS[NAME]} f
59 */
60 off (name, f) {
61 const observers = this._observers.get(name)
62 if (observers !== undefined) {
63 observers.delete(f)
64 if (observers.size === 0) {
65 this._observers.delete(name)
66 }
67 }
68 }
69
70 /**
71 * Emit a named event. All registered event listeners that listen to the
72 * specified name will receive the event.
73 *
74 * @todo This should catch exceptions
75 *
76 * @template {string} NAME
77 * @param {NAME} name The event name.
78 * @param {Parameters<EVENTS[NAME]>} args The arguments that are applied to the event listener.
79 */
80 emit (name, args) {
81 // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
82 return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args))
83 }
84
85 destroy () {
86 this._observers = map.create()
87 }
88}
89
90/* c8 ignore start */
91/**
92 * Handles named events.
93 *
94 * @deprecated
95 * @template N
96 */
97export class Observable {
98 constructor () {
99 /**
100 * Some desc.
101 * @type {Map<N, any>}
102 */
103 this._observers = map.create()
104 }
105
106 /**
107 * @param {N} name
108 * @param {function} f
109 */
110 on (name, f) {
111 map.setIfUndefined(this._observers, name, set.create).add(f)
112 }
113
114 /**
115 * @param {N} name
116 * @param {function} f
117 */
118 once (name, f) {
119 /**
120 * @param {...any} args
121 */
122 const _f = (...args) => {
123 this.off(name, _f)
124 f(...args)
125 }
126 this.on(name, _f)
127 }
128
129 /**
130 * @param {N} name
131 * @param {function} f
132 */
133 off (name, f) {
134 const observers = this._observers.get(name)
135 if (observers !== undefined) {
136 observers.delete(f)
137 if (observers.size === 0) {
138 this._observers.delete(name)
139 }
140 }
141 }
142
143 /**
144 * Emit a named event. All registered event listeners that listen to the
145 * specified name will receive the event.
146 *
147 * @todo This should catch exceptions
148 *
149 * @param {N} name The event name.
150 * @param {Array<any>} args The arguments that are applied to the event listener.
151 */
152 emit (name, args) {
153 // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
154 return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args))
155 }
156
157 destroy () {
158 this._observers = map.create()
159 }
160}
161/* c8 ignore end */