@guanghechen/observable 5.0.3 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -63
- package/lib/cjs/index.cjs +184 -42
- package/lib/esm/index.mjs +173 -43
- package/lib/types/index.d.ts +47 -30
- package/package.json +15 -24
- package/LICENSE +0 -21
- package/README.md +0 -74
package/CHANGELOG.md
CHANGED
|
@@ -1,69 +1,11 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## [5.0.3](https://github.com/guanghechen/node-scaffolds/compare/@guanghechen/observable@5.0.2...@guanghechen/observable@5.0.3) (2023-07-25)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### Performance Improvements
|
|
10
|
-
|
|
11
|
-
* 🔧 update npm script ([4ffc90a](https://github.com/guanghechen/node-scaffolds/commit/4ffc90aff15c75fcc6afeeabf4d83dbc8c1b7933))
|
|
12
|
-
* ⬆️ upgrade dependencies ([a9b6097](https://github.com/guanghechen/node-scaffolds/commit/a9b609785175e91903ca8215a807691ba6fbc419))
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## [5.0.2](https://github.com/guanghechen/node-scaffolds/compare/@guanghechen/observable@5.0.1...@guanghechen/observable@5.0.2) (2023-07-15)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
### Performance Improvements
|
|
22
|
-
|
|
23
|
-
* ⬆️ upgrade dependencies ([4f0f471](https://github.com/guanghechen/node-scaffolds/commit/4f0f4712d061099ea9d5605efeb54357e4479f6a))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
## [5.0.1](https://github.com/guanghechen/node-scaffolds/compare/@guanghechen/observable@5.0.0...@guanghechen/observable@5.0.1) (2023-05-26)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
### Performance Improvements
|
|
33
|
-
|
|
34
|
-
* 🔧 update jest config to reuse tsconfig for test ([54883f0](https://github.com/guanghechen/node-scaffolds/commit/54883f032cbba78af4609d6d4fb2b5e1ac68b518))
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# 5.0.0 (2023-05-13)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### Performance Improvements
|
|
44
|
-
|
|
45
|
-
* 📝 remove all CHANGELOG in repo ([40909ed](https://github.com/guanghechen/node-scaffolds/commit/40909ed439632e0c4b0cd817614e24fdeac2fa23))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# 5.0.0-alpha.3 (2023-05-07)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
### Performance Improvements
|
|
53
|
-
|
|
54
|
-
* :bookmark: release ([b5a3423](https://github.com/guanghechen/node-scaffolds/commit/b5a3423a3dd338d13ac4247c2651f4693f9fe363))
|
|
55
|
-
* ⬆️ upgrade devDependencies ([30a9c27](https://github.com/guanghechen/node-scaffolds/commit/30a9c27c22b8debcf8dd5b2652ec335458caf31c))
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# 5.0.0-alpha.2 (2023-05-01)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### Features
|
|
63
|
-
|
|
64
|
-
* ✨ add @guanghechen/observable ([e0ef27b](https://github.com/guanghechen/node-scaffolds/commit/e0ef27bcf22a2e9e05ff29fea05f6a4778ef3ebf))
|
|
3
|
+
All notable changes to this project will be documented in this file. See
|
|
4
|
+
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
65
5
|
|
|
6
|
+
## 6.0.1 (2024-03-09)
|
|
66
7
|
|
|
67
8
|
### Performance Improvements
|
|
68
9
|
|
|
69
|
-
|
|
10
|
+
- refactor observable
|
|
11
|
+
([777e6f4](https://github.com/guanghechen/sora/commit/777e6f41c958416a8e0f0cf9f23f9dfaac51d7c0))
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,64 +1,206 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var disposable = require('@guanghechen/disposable');
|
|
4
|
+
var observable_types = require('@guanghechen/observable.types');
|
|
5
|
+
|
|
6
|
+
const noop = (..._args) => { };
|
|
7
|
+
const noopUnsubscribable = { unsubscribe: noop };
|
|
8
|
+
const noopUnobservable = { unobserve: noop };
|
|
9
|
+
const isObservable = (obj) => {
|
|
10
|
+
if (obj === null || typeof obj !== 'object')
|
|
11
|
+
return false;
|
|
12
|
+
return (typeof Reflect.get(obj, 'dispose') === 'function' &&
|
|
13
|
+
typeof Reflect.get(obj, 'disposed') === 'boolean' &&
|
|
14
|
+
typeof Reflect.get(obj, 'subscribe') === 'function' &&
|
|
15
|
+
typeof Reflect.get(obj, 'equals') === 'function' &&
|
|
16
|
+
typeof Reflect.get(obj, 'getSnapshot') === 'function' &&
|
|
17
|
+
typeof Reflect.get(obj, 'next') === 'function');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const defaultEquals = (x, y) => Object.is(x, y);
|
|
21
|
+
class Observable extends disposable.BatchDisposable {
|
|
22
|
+
equals;
|
|
23
|
+
_delay;
|
|
4
24
|
_subscribers;
|
|
5
|
-
_equals;
|
|
6
25
|
_value;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
26
|
+
_updateTick;
|
|
27
|
+
_notifyTick;
|
|
28
|
+
_lastNotifiedValue;
|
|
29
|
+
_timer;
|
|
30
|
+
constructor(defaultValue, options = {}) {
|
|
31
|
+
super();
|
|
32
|
+
const { equals = defaultEquals } = options;
|
|
33
|
+
this._delay = Math.max(0, Number(options.delay) || 0);
|
|
34
|
+
this._subscribers = [];
|
|
11
35
|
this._value = defaultValue;
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
get disposed() {
|
|
18
|
-
return !this._available;
|
|
36
|
+
this._updateTick = 0;
|
|
37
|
+
this._notifyTick = 0;
|
|
38
|
+
this._lastNotifiedValue = undefined;
|
|
39
|
+
this._timer = undefined;
|
|
40
|
+
this.equals = equals;
|
|
19
41
|
}
|
|
20
42
|
dispose() {
|
|
21
|
-
this.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
subscriber.next(value);
|
|
43
|
+
if (this.disposed)
|
|
44
|
+
return;
|
|
45
|
+
super.dispose();
|
|
46
|
+
this._flush();
|
|
47
|
+
const batcher = new disposable.SafeBatchHandler();
|
|
48
|
+
const size = this._subscribers.length;
|
|
49
|
+
for (let i = 0; i < size; ++i) {
|
|
50
|
+
const item = this._subscribers[i];
|
|
51
|
+
if (item.inactive || item.subscriber.disposed)
|
|
52
|
+
continue;
|
|
53
|
+
batcher.run(() => item.subscriber.dispose());
|
|
33
54
|
}
|
|
55
|
+
this._subscribers.length = 0;
|
|
56
|
+
batcher.summary();
|
|
34
57
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const subscribers = this.subscribers;
|
|
38
|
-
for (const subscriber of subscribers)
|
|
39
|
-
subscriber.error(error);
|
|
40
|
-
this.dispose();
|
|
41
|
-
}
|
|
58
|
+
getSnapshot() {
|
|
59
|
+
return this._value;
|
|
42
60
|
}
|
|
43
|
-
|
|
44
|
-
if (this.
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
next(value, options) {
|
|
62
|
+
if (this.disposed) {
|
|
63
|
+
const strict = options?.strict ?? true;
|
|
64
|
+
if (strict) {
|
|
65
|
+
throw new RangeError(`Don't update a disposed observable. value: ${String(value)}.`);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
49
68
|
}
|
|
69
|
+
const force = options?.force ?? false;
|
|
70
|
+
if (!force && this.equals(value, this._value))
|
|
71
|
+
return;
|
|
72
|
+
this._value = value;
|
|
73
|
+
this._updateTick += 1;
|
|
74
|
+
this._notify();
|
|
50
75
|
}
|
|
51
76
|
subscribe(subscriber) {
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
77
|
+
if (subscriber.disposed)
|
|
78
|
+
return noopUnsubscribable;
|
|
79
|
+
if (this.disposed) {
|
|
80
|
+
subscriber.dispose();
|
|
81
|
+
return noopUnsubscribable;
|
|
55
82
|
}
|
|
83
|
+
const prevValue = this._lastNotifiedValue;
|
|
84
|
+
const value = this._value;
|
|
85
|
+
this._flush();
|
|
86
|
+
const item = { subscriber, inactive: false };
|
|
87
|
+
this._subscribers.push(item);
|
|
88
|
+
subscriber.next(value, prevValue);
|
|
56
89
|
return {
|
|
57
90
|
unsubscribe: () => {
|
|
58
|
-
|
|
91
|
+
item.inactive = true;
|
|
59
92
|
},
|
|
60
93
|
};
|
|
61
94
|
}
|
|
95
|
+
_flush() {
|
|
96
|
+
if (this._notifyTick < this._updateTick) {
|
|
97
|
+
if (this._timer !== undefined) {
|
|
98
|
+
clearTimeout(this._timer);
|
|
99
|
+
this._timer = undefined;
|
|
100
|
+
}
|
|
101
|
+
this._notifyImmediate();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
_notify() {
|
|
105
|
+
if (this._notifyTick < this._updateTick) {
|
|
106
|
+
if (this._delay <= 0) {
|
|
107
|
+
this._notifyImmediate();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (this._timer === undefined) {
|
|
111
|
+
this._timer = setTimeout(() => {
|
|
112
|
+
try {
|
|
113
|
+
this._notifyImmediate();
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
this._timer = undefined;
|
|
117
|
+
}
|
|
118
|
+
this._notify();
|
|
119
|
+
}, this._delay);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
_notifyImmediate() {
|
|
124
|
+
const prevValue = this._lastNotifiedValue;
|
|
125
|
+
const value = this._value;
|
|
126
|
+
this._lastNotifiedValue = value;
|
|
127
|
+
this._notifyTick = this._updateTick;
|
|
128
|
+
const batcher = new disposable.SafeBatchHandler();
|
|
129
|
+
const subscribers = this._subscribers;
|
|
130
|
+
const size = subscribers.length;
|
|
131
|
+
for (let i = 0; i < size; ++i) {
|
|
132
|
+
const subscriber = subscribers[i];
|
|
133
|
+
if (subscriber.inactive || subscriber.subscriber.disposed)
|
|
134
|
+
continue;
|
|
135
|
+
batcher.run(() => subscriber.subscriber.next(value, prevValue));
|
|
136
|
+
}
|
|
137
|
+
batcher.summary();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
class Subscriber {
|
|
142
|
+
_onDispose;
|
|
143
|
+
_onNext;
|
|
144
|
+
_disposed;
|
|
145
|
+
constructor(options) {
|
|
146
|
+
this._onDispose = options?.onDispose ?? noop;
|
|
147
|
+
this._onNext = options.onNext;
|
|
148
|
+
this._disposed = false;
|
|
149
|
+
}
|
|
150
|
+
get disposed() {
|
|
151
|
+
return this._disposed;
|
|
152
|
+
}
|
|
153
|
+
dispose() {
|
|
154
|
+
if (this._disposed)
|
|
155
|
+
return;
|
|
156
|
+
this._disposed = true;
|
|
157
|
+
this._onDispose();
|
|
158
|
+
}
|
|
159
|
+
next(value, prevValue) {
|
|
160
|
+
if (this._disposed)
|
|
161
|
+
return;
|
|
162
|
+
this._onNext(value, prevValue);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const equals = (x, y) => x === y;
|
|
167
|
+
class Ticker extends Observable {
|
|
168
|
+
constructor(options = {}) {
|
|
169
|
+
const { start = 0, delay } = options;
|
|
170
|
+
super(start, { delay, equals });
|
|
171
|
+
}
|
|
172
|
+
tick(options) {
|
|
173
|
+
this.next(this._value + 1, options);
|
|
174
|
+
}
|
|
175
|
+
observe(observable, options) {
|
|
176
|
+
if (this.disposed) {
|
|
177
|
+
const strict = options?.strict ?? true;
|
|
178
|
+
if (strict)
|
|
179
|
+
throw new RangeError('[Ticker.observe] the ticker has been disposed.');
|
|
180
|
+
return noopUnobservable;
|
|
181
|
+
}
|
|
182
|
+
if (observable.disposed)
|
|
183
|
+
return noopUnobservable;
|
|
184
|
+
const subscriber = new Subscriber({
|
|
185
|
+
onNext: () => this.tick(),
|
|
186
|
+
onDispose: () => unsubscribable.unsubscribe(),
|
|
187
|
+
});
|
|
188
|
+
const unsubscribable = observable.subscribe(subscriber);
|
|
189
|
+
this.registerDisposable(subscriber);
|
|
190
|
+
return { unobserve: () => unsubscribable.unsubscribe() };
|
|
191
|
+
}
|
|
62
192
|
}
|
|
63
193
|
|
|
64
194
|
exports.Observable = Observable;
|
|
195
|
+
exports.Subscriber = Subscriber;
|
|
196
|
+
exports.Ticker = Ticker;
|
|
197
|
+
exports.isObservable = isObservable;
|
|
198
|
+
exports.noop = noop;
|
|
199
|
+
exports.noopUnobservable = noopUnobservable;
|
|
200
|
+
exports.noopUnsubscribable = noopUnsubscribable;
|
|
201
|
+
Object.keys(observable_types).forEach(function (k) {
|
|
202
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
203
|
+
enumerable: true,
|
|
204
|
+
get: function () { return observable_types[k]; }
|
|
205
|
+
});
|
|
206
|
+
});
|
package/lib/esm/index.mjs
CHANGED
|
@@ -1,62 +1,192 @@
|
|
|
1
|
-
|
|
1
|
+
import { BatchDisposable, SafeBatchHandler } from '@guanghechen/disposable';
|
|
2
|
+
export * from '@guanghechen/observable.types';
|
|
3
|
+
|
|
4
|
+
const noop = (..._args) => { };
|
|
5
|
+
const noopUnsubscribable = { unsubscribe: noop };
|
|
6
|
+
const noopUnobservable = { unobserve: noop };
|
|
7
|
+
const isObservable = (obj) => {
|
|
8
|
+
if (obj === null || typeof obj !== 'object')
|
|
9
|
+
return false;
|
|
10
|
+
return (typeof Reflect.get(obj, 'dispose') === 'function' &&
|
|
11
|
+
typeof Reflect.get(obj, 'disposed') === 'boolean' &&
|
|
12
|
+
typeof Reflect.get(obj, 'subscribe') === 'function' &&
|
|
13
|
+
typeof Reflect.get(obj, 'equals') === 'function' &&
|
|
14
|
+
typeof Reflect.get(obj, 'getSnapshot') === 'function' &&
|
|
15
|
+
typeof Reflect.get(obj, 'next') === 'function');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const defaultEquals = (x, y) => Object.is(x, y);
|
|
19
|
+
class Observable extends BatchDisposable {
|
|
20
|
+
equals;
|
|
21
|
+
_delay;
|
|
2
22
|
_subscribers;
|
|
3
|
-
_equals;
|
|
4
23
|
_value;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
24
|
+
_updateTick;
|
|
25
|
+
_notifyTick;
|
|
26
|
+
_lastNotifiedValue;
|
|
27
|
+
_timer;
|
|
28
|
+
constructor(defaultValue, options = {}) {
|
|
29
|
+
super();
|
|
30
|
+
const { equals = defaultEquals } = options;
|
|
31
|
+
this._delay = Math.max(0, Number(options.delay) || 0);
|
|
32
|
+
this._subscribers = [];
|
|
9
33
|
this._value = defaultValue;
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
get disposed() {
|
|
16
|
-
return !this._available;
|
|
34
|
+
this._updateTick = 0;
|
|
35
|
+
this._notifyTick = 0;
|
|
36
|
+
this._lastNotifiedValue = undefined;
|
|
37
|
+
this._timer = undefined;
|
|
38
|
+
this.equals = equals;
|
|
17
39
|
}
|
|
18
40
|
dispose() {
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
subscriber.next(value);
|
|
41
|
+
if (this.disposed)
|
|
42
|
+
return;
|
|
43
|
+
super.dispose();
|
|
44
|
+
this._flush();
|
|
45
|
+
const batcher = new SafeBatchHandler();
|
|
46
|
+
const size = this._subscribers.length;
|
|
47
|
+
for (let i = 0; i < size; ++i) {
|
|
48
|
+
const item = this._subscribers[i];
|
|
49
|
+
if (item.inactive || item.subscriber.disposed)
|
|
50
|
+
continue;
|
|
51
|
+
batcher.run(() => item.subscriber.dispose());
|
|
31
52
|
}
|
|
53
|
+
this._subscribers.length = 0;
|
|
54
|
+
batcher.summary();
|
|
32
55
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const subscribers = this.subscribers;
|
|
36
|
-
for (const subscriber of subscribers)
|
|
37
|
-
subscriber.error(error);
|
|
38
|
-
this.dispose();
|
|
39
|
-
}
|
|
56
|
+
getSnapshot() {
|
|
57
|
+
return this._value;
|
|
40
58
|
}
|
|
41
|
-
|
|
42
|
-
if (this.
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
next(value, options) {
|
|
60
|
+
if (this.disposed) {
|
|
61
|
+
const strict = options?.strict ?? true;
|
|
62
|
+
if (strict) {
|
|
63
|
+
throw new RangeError(`Don't update a disposed observable. value: ${String(value)}.`);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
47
66
|
}
|
|
67
|
+
const force = options?.force ?? false;
|
|
68
|
+
if (!force && this.equals(value, this._value))
|
|
69
|
+
return;
|
|
70
|
+
this._value = value;
|
|
71
|
+
this._updateTick += 1;
|
|
72
|
+
this._notify();
|
|
48
73
|
}
|
|
49
74
|
subscribe(subscriber) {
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
if (subscriber.disposed)
|
|
76
|
+
return noopUnsubscribable;
|
|
77
|
+
if (this.disposed) {
|
|
78
|
+
subscriber.dispose();
|
|
79
|
+
return noopUnsubscribable;
|
|
53
80
|
}
|
|
81
|
+
const prevValue = this._lastNotifiedValue;
|
|
82
|
+
const value = this._value;
|
|
83
|
+
this._flush();
|
|
84
|
+
const item = { subscriber, inactive: false };
|
|
85
|
+
this._subscribers.push(item);
|
|
86
|
+
subscriber.next(value, prevValue);
|
|
54
87
|
return {
|
|
55
88
|
unsubscribe: () => {
|
|
56
|
-
|
|
89
|
+
item.inactive = true;
|
|
57
90
|
},
|
|
58
91
|
};
|
|
59
92
|
}
|
|
93
|
+
_flush() {
|
|
94
|
+
if (this._notifyTick < this._updateTick) {
|
|
95
|
+
if (this._timer !== undefined) {
|
|
96
|
+
clearTimeout(this._timer);
|
|
97
|
+
this._timer = undefined;
|
|
98
|
+
}
|
|
99
|
+
this._notifyImmediate();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
_notify() {
|
|
103
|
+
if (this._notifyTick < this._updateTick) {
|
|
104
|
+
if (this._delay <= 0) {
|
|
105
|
+
this._notifyImmediate();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (this._timer === undefined) {
|
|
109
|
+
this._timer = setTimeout(() => {
|
|
110
|
+
try {
|
|
111
|
+
this._notifyImmediate();
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
this._timer = undefined;
|
|
115
|
+
}
|
|
116
|
+
this._notify();
|
|
117
|
+
}, this._delay);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
_notifyImmediate() {
|
|
122
|
+
const prevValue = this._lastNotifiedValue;
|
|
123
|
+
const value = this._value;
|
|
124
|
+
this._lastNotifiedValue = value;
|
|
125
|
+
this._notifyTick = this._updateTick;
|
|
126
|
+
const batcher = new SafeBatchHandler();
|
|
127
|
+
const subscribers = this._subscribers;
|
|
128
|
+
const size = subscribers.length;
|
|
129
|
+
for (let i = 0; i < size; ++i) {
|
|
130
|
+
const subscriber = subscribers[i];
|
|
131
|
+
if (subscriber.inactive || subscriber.subscriber.disposed)
|
|
132
|
+
continue;
|
|
133
|
+
batcher.run(() => subscriber.subscriber.next(value, prevValue));
|
|
134
|
+
}
|
|
135
|
+
batcher.summary();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
class Subscriber {
|
|
140
|
+
_onDispose;
|
|
141
|
+
_onNext;
|
|
142
|
+
_disposed;
|
|
143
|
+
constructor(options) {
|
|
144
|
+
this._onDispose = options?.onDispose ?? noop;
|
|
145
|
+
this._onNext = options.onNext;
|
|
146
|
+
this._disposed = false;
|
|
147
|
+
}
|
|
148
|
+
get disposed() {
|
|
149
|
+
return this._disposed;
|
|
150
|
+
}
|
|
151
|
+
dispose() {
|
|
152
|
+
if (this._disposed)
|
|
153
|
+
return;
|
|
154
|
+
this._disposed = true;
|
|
155
|
+
this._onDispose();
|
|
156
|
+
}
|
|
157
|
+
next(value, prevValue) {
|
|
158
|
+
if (this._disposed)
|
|
159
|
+
return;
|
|
160
|
+
this._onNext(value, prevValue);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const equals = (x, y) => x === y;
|
|
165
|
+
class Ticker extends Observable {
|
|
166
|
+
constructor(options = {}) {
|
|
167
|
+
const { start = 0, delay } = options;
|
|
168
|
+
super(start, { delay, equals });
|
|
169
|
+
}
|
|
170
|
+
tick(options) {
|
|
171
|
+
this.next(this._value + 1, options);
|
|
172
|
+
}
|
|
173
|
+
observe(observable, options) {
|
|
174
|
+
if (this.disposed) {
|
|
175
|
+
const strict = options?.strict ?? true;
|
|
176
|
+
if (strict)
|
|
177
|
+
throw new RangeError('[Ticker.observe] the ticker has been disposed.');
|
|
178
|
+
return noopUnobservable;
|
|
179
|
+
}
|
|
180
|
+
if (observable.disposed)
|
|
181
|
+
return noopUnobservable;
|
|
182
|
+
const subscriber = new Subscriber({
|
|
183
|
+
onNext: () => this.tick(),
|
|
184
|
+
onDispose: () => unsubscribable.unsubscribe(),
|
|
185
|
+
});
|
|
186
|
+
const unsubscribable = observable.subscribe(subscriber);
|
|
187
|
+
this.registerDisposable(subscriber);
|
|
188
|
+
return { unobserve: () => unsubscribable.unsubscribe() };
|
|
189
|
+
}
|
|
60
190
|
}
|
|
61
191
|
|
|
62
|
-
export { Observable };
|
|
192
|
+
export { Observable, Subscriber, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,38 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { BatchDisposable } from '@guanghechen/disposable';
|
|
2
|
+
import { IObservable, IEquals, IObservableOptions, IObservableNextOptions, ISubscriber, IUnsubscribable, ITicker, ITickerOptions, ITickerObserveOptions, IUnobservable } from '@guanghechen/observable.types';
|
|
3
|
+
export * from '@guanghechen/observable.types';
|
|
4
|
+
|
|
5
|
+
interface IObservableSubscriber<T> {
|
|
6
|
+
readonly subscriber: ISubscriber<T>;
|
|
7
|
+
inactive: boolean;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
declare class Observable<T> extends BatchDisposable implements IObservable<T> {
|
|
10
|
+
readonly equals: IEquals<T>;
|
|
11
|
+
protected readonly _delay: number;
|
|
12
|
+
protected readonly _subscribers: Array<IObservableSubscriber<T>>;
|
|
13
|
+
protected _value: T;
|
|
14
|
+
protected _updateTick: number;
|
|
15
|
+
protected _notifyTick: number;
|
|
16
|
+
protected _lastNotifiedValue: T | undefined;
|
|
17
|
+
protected _timer: ReturnType<typeof setTimeout> | undefined;
|
|
18
|
+
constructor(defaultValue: T, options?: IObservableOptions<T>);
|
|
19
|
+
dispose(): void;
|
|
20
|
+
getSnapshot(): T;
|
|
21
|
+
next(value: T, options?: IObservableNextOptions): void;
|
|
10
22
|
subscribe(subscriber: ISubscriber<T>): IUnsubscribable;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
error(err: any): void;
|
|
15
|
-
complete(): void;
|
|
23
|
+
protected _flush(): void;
|
|
24
|
+
protected _notify(): void;
|
|
25
|
+
protected _notifyImmediate(): void;
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
type IOnDisposable = () => void;
|
|
29
|
+
type IOnNext<T> = (value: T, prevValue: T | undefined) => void;
|
|
30
|
+
interface ISubscriberOptions<T> {
|
|
31
|
+
readonly onNext: IOnNext<T>;
|
|
32
|
+
readonly onDispose?: IOnDisposable;
|
|
33
|
+
}
|
|
34
|
+
declare class Subscriber<T> implements ISubscriber<T> {
|
|
35
|
+
protected readonly _onDispose: IOnDisposable;
|
|
36
|
+
protected readonly _onNext: IOnNext<T>;
|
|
37
|
+
protected _disposed: boolean;
|
|
38
|
+
constructor(options: ISubscriberOptions<T>);
|
|
25
39
|
get disposed(): boolean;
|
|
26
40
|
dispose(): void;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 1. Update observable current state.
|
|
30
|
-
* 2. Notify all subscribers if the value is changed.
|
|
31
|
-
*/
|
|
32
|
-
next(value: T): void;
|
|
33
|
-
error(error: unknown): void;
|
|
34
|
-
complete(): void;
|
|
35
|
-
subscribe(subscriber: ISubscriber<T>): IUnsubscribable;
|
|
41
|
+
next(value: T, prevValue: T | undefined): void;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
declare class Ticker extends Observable<number> implements ITicker {
|
|
45
|
+
constructor(options?: ITickerOptions);
|
|
46
|
+
tick(options?: IObservableNextOptions): void;
|
|
47
|
+
observe<T>(observable: IObservable<T>, options?: ITickerObserveOptions): IUnobservable;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare const noop: (..._args: any[]) => void;
|
|
51
|
+
declare const noopUnsubscribable: IUnsubscribable;
|
|
52
|
+
declare const noopUnobservable: IUnobservable;
|
|
53
|
+
declare const isObservable: (obj: unknown) => obj is IObservable<unknown>;
|
|
54
|
+
|
|
55
|
+
export { type ISubscriberOptions, Observable, Subscriber, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
|
package/package.json
CHANGED
|
@@ -1,33 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guanghechen/observable",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A simple observable for subscript/notification pattern.",
|
|
3
|
+
"version": "6.0.1",
|
|
5
4
|
"author": {
|
|
6
5
|
"name": "guanghechen",
|
|
7
6
|
"url": "https://github.com/guanghechen/"
|
|
8
7
|
},
|
|
9
8
|
"repository": {
|
|
10
9
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/guanghechen/
|
|
10
|
+
"url": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.0.0",
|
|
12
11
|
"directory": "packages/observable"
|
|
13
12
|
},
|
|
14
|
-
"homepage": "https://github.com/guanghechen/
|
|
13
|
+
"homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.0.0/packages/observable#readme",
|
|
15
14
|
"keywords": [
|
|
16
15
|
"observable"
|
|
17
16
|
],
|
|
18
17
|
"type": "module",
|
|
19
|
-
"main": "./lib/cjs/index.cjs",
|
|
20
|
-
"module": "./lib/esm/index.mjs",
|
|
21
18
|
"exports": {
|
|
22
|
-
"
|
|
23
|
-
"require": "./lib/cjs/index.cjs"
|
|
19
|
+
"types": "./lib/types/index.d.ts",
|
|
20
|
+
"require": "./lib/cjs/index.cjs",
|
|
21
|
+
"import": "./lib/esm/index.mjs"
|
|
24
22
|
},
|
|
25
|
-
"types": "lib/types/index.d.ts",
|
|
26
|
-
"
|
|
23
|
+
"types": "./lib/types/index.d.ts",
|
|
24
|
+
"main": "./lib/cjs/index.cjs",
|
|
25
|
+
"module": "./lib/esm/index.mjs",
|
|
26
|
+
"source": "./src/index.ts",
|
|
27
27
|
"license": "MIT",
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">= 16.0.0"
|
|
30
|
-
},
|
|
31
28
|
"files": [
|
|
32
29
|
"lib/",
|
|
33
30
|
"!lib/**/*.map",
|
|
@@ -36,16 +33,10 @@
|
|
|
36
33
|
"LICENSE",
|
|
37
34
|
"README.md"
|
|
38
35
|
],
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"cross-env": "^7.0.3",
|
|
46
|
-
"jest": "^29.6.1",
|
|
47
|
-
"rimraf": "^5.0.1",
|
|
48
|
-
"rollup": "^3.26.3"
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@guanghechen/disposable": "^1.0.0-alpha.3",
|
|
38
|
+
"@guanghechen/error.types": "^1.0.1",
|
|
39
|
+
"@guanghechen/observable.types": "^6.0.1"
|
|
49
40
|
},
|
|
50
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "8f4595ed8e3b21ac509a16aeb52cbb686636d9f0"
|
|
51
42
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020-2023 guanghechen (https://github.com/guanghechen)
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
<header>
|
|
2
|
-
<h1 align="center">
|
|
3
|
-
<a href="https://github.com/guanghechen/node-scaffolds/tree/@guanghechen/observable@5.0.3/packages/observable#readme">@guanghechen/observable</a>
|
|
4
|
-
</h1>
|
|
5
|
-
<div align="center">
|
|
6
|
-
<a href="https://www.npmjs.com/package/@guanghechen/observable">
|
|
7
|
-
<img
|
|
8
|
-
alt="Npm Version"
|
|
9
|
-
src="https://img.shields.io/npm/v/@guanghechen/observable.svg"
|
|
10
|
-
/>
|
|
11
|
-
</a>
|
|
12
|
-
<a href="https://www.npmjs.com/package/@guanghechen/observable">
|
|
13
|
-
<img
|
|
14
|
-
alt="Npm Download"
|
|
15
|
-
src="https://img.shields.io/npm/dm/@guanghechen/observable.svg"
|
|
16
|
-
/>
|
|
17
|
-
</a>
|
|
18
|
-
<a href="https://www.npmjs.com/package/@guanghechen/observable">
|
|
19
|
-
<img
|
|
20
|
-
alt="Npm License"
|
|
21
|
-
src="https://img.shields.io/npm/l/@guanghechen/observable.svg"
|
|
22
|
-
/>
|
|
23
|
-
</a>
|
|
24
|
-
<a href="#install">
|
|
25
|
-
<img
|
|
26
|
-
alt="Module formats: cjs, esm"
|
|
27
|
-
src="https://img.shields.io/badge/module_formats-cjs%2C%20esm-green.svg"
|
|
28
|
-
/>
|
|
29
|
-
</a>
|
|
30
|
-
<a href="https://github.com/nodejs/node">
|
|
31
|
-
<img
|
|
32
|
-
alt="Node.js Version"
|
|
33
|
-
src="https://img.shields.io/node/v/@guanghechen/observable"
|
|
34
|
-
/>
|
|
35
|
-
</a>
|
|
36
|
-
<a href="https://github.com/facebook/jest">
|
|
37
|
-
<img
|
|
38
|
-
alt="Tested with Jest"
|
|
39
|
-
src="https://img.shields.io/badge/tested_with-jest-9c465e.svg"
|
|
40
|
-
/>
|
|
41
|
-
</a>
|
|
42
|
-
<a href="https://github.com/prettier/prettier">
|
|
43
|
-
<img
|
|
44
|
-
alt="Code Style: prettier"
|
|
45
|
-
src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"
|
|
46
|
-
/>
|
|
47
|
-
</a>
|
|
48
|
-
</div>
|
|
49
|
-
</header>
|
|
50
|
-
<br/>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
A simple observable for subscript/notification pattern.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## Install
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
npm install --save @guanghechen/observable
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
* yarn
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
yarn add @guanghechen/observable
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## Usage
|
|
70
|
-
|
|
71
|
-
* Basic
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
[homepage]: https://github.com/guanghechen/node-scaffolds/tree/@guanghechen/observable@5.0.3/packages/observable#readme
|