@guanghechen/observable 6.0.3 → 6.1.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 CHANGED
@@ -3,6 +3,35 @@
3
3
  All notable changes to this project will be documented in this file. See
4
4
  [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [6.1.1](https://github.com/guanghechen/sora/compare/@guanghechen/observable@6.1.0...@guanghechen/observable@6.1.1) (2024-03-10)
7
+
8
+ ### Bug Fixes
9
+
10
+ - 🐛 cannot access 'unsubscribable' before initialization
11
+ ([78ba9ed](https://github.com/guanghechen/sora/commit/78ba9ed20e47cb0a5f107c3c34783c8b2da9263c))
12
+
13
+ # Change Log
14
+
15
+ All notable changes to this project will be documented in this file. See
16
+ [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
17
+
18
+ # [6.1.0](https://github.com/guanghechen/sora/compare/@guanghechen/observable@6.0.3...@guanghechen/observable@6.1.0) (2024-03-10)
19
+
20
+ ### Features
21
+
22
+ - ✨ add @guanghechen/subscriber
23
+ ([ba57528](https://github.com/guanghechen/sora/commit/ba575283cd159e21896dfab062eff0b5da216757))
24
+
25
+ ### Performance Improvements
26
+
27
+ - 🎨 refaactor observable with subscriber & remove observable.types
28
+ ([bac2118](https://github.com/guanghechen/sora/commit/bac211888713cac920154efb593dbbcf903ab33e))
29
+
30
+ # Change Log
31
+
32
+ All notable changes to this project will be documented in this file. See
33
+ [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
34
+
6
35
  ## [6.0.3](https://github.com/guanghechen/sora/compare/@guanghechen/observable@6.0.2...@guanghechen/observable@6.0.3) (2024-03-10)
7
36
 
8
37
  ### Performance Improvements
package/lib/cjs/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var disposable = require('@guanghechen/disposable');
4
- var observable_types = require('@guanghechen/observable.types');
4
+ var subscriber = require('@guanghechen/subscriber');
5
5
 
6
6
  const noop = (..._args) => { };
7
7
  const noopUnsubscribable = { unsubscribe: noop };
@@ -31,7 +31,7 @@ class Observable extends disposable.BatchDisposable {
31
31
  super();
32
32
  const { equals = defaultEquals } = options;
33
33
  this._delay = Math.max(0, Number(options.delay) || 0);
34
- this._subscribers = [];
34
+ this._subscribers = new subscriber.Subscribers();
35
35
  this._value = defaultValue;
36
36
  this._updateTick = 0;
37
37
  this._notifyTick = 0;
@@ -44,19 +44,7 @@ class Observable extends disposable.BatchDisposable {
44
44
  return;
45
45
  super.dispose();
46
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());
54
- }
55
- for (const item of this._subscribers)
56
- item.inactive = true;
57
- this._subscribers.length = 0;
58
- batcher.summary('[observable] Encountered errors while disposing.');
59
- batcher.cleanup();
47
+ this._subscribers.dispose();
60
48
  }
61
49
  getSnapshot() {
62
50
  return this._value;
@@ -87,14 +75,8 @@ class Observable extends disposable.BatchDisposable {
87
75
  return noopUnsubscribable;
88
76
  }
89
77
  this._flush();
90
- const item = { subscriber, inactive: false };
91
- this._subscribers.push(item);
92
78
  subscriber.next(value, prevValue);
93
- return {
94
- unsubscribe: () => {
95
- item.inactive = true;
96
- },
97
- };
79
+ return this._subscribers.subscribe(subscriber);
98
80
  }
99
81
  _flush() {
100
82
  if (this._notifyTick < this._updateTick) {
@@ -129,42 +111,7 @@ class Observable extends disposable.BatchDisposable {
129
111
  const value = this._value;
130
112
  this._lastNotifiedValue = value;
131
113
  this._notifyTick = this._updateTick;
132
- const batcher = new disposable.SafeBatchHandler();
133
- const subscribers = this._subscribers;
134
- const size = subscribers.length;
135
- for (let i = 0; i < size; ++i) {
136
- const subscriber = subscribers[i];
137
- if (subscriber.inactive || subscriber.subscriber.disposed)
138
- continue;
139
- batcher.run(() => subscriber.subscriber.next(value, prevValue));
140
- }
141
- batcher.summary('[observable] Encountered errors while notifying subscribers.');
142
- batcher.cleanup();
143
- }
144
- }
145
-
146
- class Subscriber {
147
- _onDispose;
148
- _onNext;
149
- _disposed;
150
- constructor(options) {
151
- this._onDispose = options?.onDispose ?? noop;
152
- this._onNext = options.onNext;
153
- this._disposed = false;
154
- }
155
- get disposed() {
156
- return this._disposed;
157
- }
158
- dispose() {
159
- if (this._disposed)
160
- return;
161
- this._disposed = true;
162
- this._onDispose();
163
- }
164
- next(value, prevValue) {
165
- if (this._disposed)
166
- return;
167
- this._onNext(value, prevValue);
114
+ this._subscribers.notify(value, prevValue);
168
115
  }
169
116
  }
170
117
 
@@ -186,25 +133,20 @@ class Ticker extends Observable {
186
133
  }
187
134
  if (observable.disposed)
188
135
  return noopUnobservable;
189
- const subscriber = new Subscriber({ onNext: () => this.tick() });
190
- const unsubscribable = observable.subscribe(subscriber);
191
- const disposable$1 = new disposable.Disposable(() => unsubscribable.unsubscribe());
192
- this.registerDisposable(subscriber);
136
+ const subscriber$1 = new subscriber.Subscriber({ onNext: () => this.tick() });
137
+ const unsubscribable = observable.subscribe(subscriber$1);
138
+ const disposable$1 = new disposable.Disposable(() => {
139
+ subscriber$1.dispose();
140
+ unsubscribable.unsubscribe();
141
+ });
193
142
  this.registerDisposable(disposable$1);
194
143
  return { unobserve: () => disposable$1.dispose() };
195
144
  }
196
145
  }
197
146
 
198
147
  exports.Observable = Observable;
199
- exports.Subscriber = Subscriber;
200
148
  exports.Ticker = Ticker;
201
149
  exports.isObservable = isObservable;
202
150
  exports.noop = noop;
203
151
  exports.noopUnobservable = noopUnobservable;
204
152
  exports.noopUnsubscribable = noopUnsubscribable;
205
- Object.keys(observable_types).forEach(function (k) {
206
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
207
- enumerable: true,
208
- get: function () { return observable_types[k]; }
209
- });
210
- });
package/lib/esm/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { BatchDisposable, SafeBatchHandler, Disposable } from '@guanghechen/disposable';
2
- export * from '@guanghechen/observable.types';
1
+ import { BatchDisposable, Disposable } from '@guanghechen/disposable';
2
+ import { Subscribers, Subscriber } from '@guanghechen/subscriber';
3
3
 
4
4
  const noop = (..._args) => { };
5
5
  const noopUnsubscribable = { unsubscribe: noop };
@@ -29,7 +29,7 @@ class Observable extends BatchDisposable {
29
29
  super();
30
30
  const { equals = defaultEquals } = options;
31
31
  this._delay = Math.max(0, Number(options.delay) || 0);
32
- this._subscribers = [];
32
+ this._subscribers = new Subscribers();
33
33
  this._value = defaultValue;
34
34
  this._updateTick = 0;
35
35
  this._notifyTick = 0;
@@ -42,19 +42,7 @@ class Observable extends BatchDisposable {
42
42
  return;
43
43
  super.dispose();
44
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());
52
- }
53
- for (const item of this._subscribers)
54
- item.inactive = true;
55
- this._subscribers.length = 0;
56
- batcher.summary('[observable] Encountered errors while disposing.');
57
- batcher.cleanup();
45
+ this._subscribers.dispose();
58
46
  }
59
47
  getSnapshot() {
60
48
  return this._value;
@@ -85,14 +73,8 @@ class Observable extends BatchDisposable {
85
73
  return noopUnsubscribable;
86
74
  }
87
75
  this._flush();
88
- const item = { subscriber, inactive: false };
89
- this._subscribers.push(item);
90
76
  subscriber.next(value, prevValue);
91
- return {
92
- unsubscribe: () => {
93
- item.inactive = true;
94
- },
95
- };
77
+ return this._subscribers.subscribe(subscriber);
96
78
  }
97
79
  _flush() {
98
80
  if (this._notifyTick < this._updateTick) {
@@ -127,42 +109,7 @@ class Observable extends BatchDisposable {
127
109
  const value = this._value;
128
110
  this._lastNotifiedValue = value;
129
111
  this._notifyTick = this._updateTick;
130
- const batcher = new SafeBatchHandler();
131
- const subscribers = this._subscribers;
132
- const size = subscribers.length;
133
- for (let i = 0; i < size; ++i) {
134
- const subscriber = subscribers[i];
135
- if (subscriber.inactive || subscriber.subscriber.disposed)
136
- continue;
137
- batcher.run(() => subscriber.subscriber.next(value, prevValue));
138
- }
139
- batcher.summary('[observable] Encountered errors while notifying subscribers.');
140
- batcher.cleanup();
141
- }
142
- }
143
-
144
- class Subscriber {
145
- _onDispose;
146
- _onNext;
147
- _disposed;
148
- constructor(options) {
149
- this._onDispose = options?.onDispose ?? noop;
150
- this._onNext = options.onNext;
151
- this._disposed = false;
152
- }
153
- get disposed() {
154
- return this._disposed;
155
- }
156
- dispose() {
157
- if (this._disposed)
158
- return;
159
- this._disposed = true;
160
- this._onDispose();
161
- }
162
- next(value, prevValue) {
163
- if (this._disposed)
164
- return;
165
- this._onNext(value, prevValue);
112
+ this._subscribers.notify(value, prevValue);
166
113
  }
167
114
  }
168
115
 
@@ -186,11 +133,13 @@ class Ticker extends Observable {
186
133
  return noopUnobservable;
187
134
  const subscriber = new Subscriber({ onNext: () => this.tick() });
188
135
  const unsubscribable = observable.subscribe(subscriber);
189
- const disposable = new Disposable(() => unsubscribable.unsubscribe());
190
- this.registerDisposable(subscriber);
136
+ const disposable = new Disposable(() => {
137
+ subscriber.dispose();
138
+ unsubscribable.unsubscribe();
139
+ });
191
140
  this.registerDisposable(disposable);
192
141
  return { unobserve: () => disposable.dispose() };
193
142
  }
194
143
  }
195
144
 
196
- export { Observable, Subscriber, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
145
+ export { Observable, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
@@ -1,15 +1,51 @@
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';
1
+ import { IBatchDisposable, BatchDisposable } from '@guanghechen/disposable';
2
+ import { ISubscribable, ISubscribers, ISubscriber, IUnsubscribable } from '@guanghechen/subscriber';
4
3
 
5
- interface IObservableSubscriber<T> {
6
- readonly subscriber: ISubscriber<T>;
7
- inactive: boolean;
4
+ type IEquals<T> = (x: T, y: T) => boolean;
5
+ interface IObservableOptions<T> {
6
+ /**
7
+ * Delay before notifying subscribers. (milliseconds)
8
+ */
9
+ readonly delay?: number;
10
+ /**
11
+ * Determine whether the two values are equal.
12
+ */
13
+ readonly equals?: IEquals<T>;
8
14
  }
15
+ interface IObservableNextOptions {
16
+ /**
17
+ * Whether to throw an error if the observable disposed.
18
+ * @default true
19
+ */
20
+ readonly strict?: boolean;
21
+ /**
22
+ * Force trigger the notification of subscribers even the next value is equals to the current value.
23
+ * @default false
24
+ */
25
+ readonly force?: boolean;
26
+ }
27
+ interface IObservable<T> extends IBatchDisposable, ISubscribable<T> {
28
+ readonly equals: IEquals<T>;
29
+ getSnapshot(): T;
30
+ next(value: T, options?: IObservableNextOptions): void;
31
+ }
32
+ type IValueList<T extends Array<IObservable<any>>> = {
33
+ [K in keyof T]: T[K] extends IObservable<infer U> ? U : never;
34
+ };
35
+ type IValueMap<T extends object> = {
36
+ [key in keyof T]: T[key] extends IObservable<infer U> ? U : never;
37
+ };
38
+ type IObservableRecord<T extends object> = {
39
+ [key in keyof T]: T[key] extends IObservable<any> ? T[key] : never;
40
+ };
41
+ type IObservableKey<T extends object> = keyof {
42
+ [key in keyof T]: T[key] extends IObservable<any> ? key : never;
43
+ };
44
+
9
45
  declare class Observable<T> extends BatchDisposable implements IObservable<T> {
10
46
  readonly equals: IEquals<T>;
11
47
  protected readonly _delay: number;
12
- protected readonly _subscribers: Array<IObservableSubscriber<T>>;
48
+ protected readonly _subscribers: ISubscribers<T>;
13
49
  protected _value: T;
14
50
  protected _updateTick: number;
15
51
  protected _notifyTick: number;
@@ -25,20 +61,43 @@ declare class Observable<T> extends BatchDisposable implements IObservable<T> {
25
61
  protected _notifyImmediate(): void;
26
62
  }
27
63
 
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>);
39
- get disposed(): boolean;
40
- dispose(): void;
41
- next(value: T, prevValue: T | undefined): void;
64
+ /**
65
+ * A unobservable can be unobserved.
66
+ */
67
+ interface IUnobservable {
68
+ /**
69
+ * Won't get notified from the observable after unobserved, but it could still not be completed.
70
+ */
71
+ unobserve(): void;
72
+ }
73
+ interface ITickerOptions {
74
+ /**
75
+ * The initial value of the ticker.
76
+ */
77
+ readonly start?: number;
78
+ /**
79
+ * Delay before notifying subscribers. (milliseconds)
80
+ */
81
+ readonly delay?: number;
82
+ }
83
+ interface ITickerObserveOptions {
84
+ /**
85
+ * Whether to throw an error if the observable disposed.
86
+ * @default true
87
+ */
88
+ readonly strict?: boolean;
89
+ }
90
+ interface ITicker extends IObservable<number> {
91
+ /**
92
+ * Trigger the ticker to update the value.
93
+ */
94
+ tick(options?: IObservableNextOptions): void;
95
+ /**
96
+ * Subscribe the change of an observable.
97
+ * @param observable
98
+ * @param options
99
+ */
100
+ observe(observable: IObservable<any>, options?: ITickerObserveOptions): void;
42
101
  }
43
102
 
44
103
  declare class Ticker extends Observable<number> implements ITicker {
@@ -52,4 +111,4 @@ declare const noopUnsubscribable: IUnsubscribable;
52
111
  declare const noopUnobservable: IUnobservable;
53
112
  declare const isObservable: (obj: unknown) => obj is IObservable<unknown>;
54
113
 
55
- export { type ISubscriberOptions, Observable, Subscriber, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
114
+ export { type IEquals, type IObservable, type IObservableKey, type IObservableNextOptions, type IObservableOptions, type IObservableRecord, type ITicker, type ITickerObserveOptions, type ITickerOptions, type IUnobservable, type IValueList, type IValueMap, Observable, Ticker, isObservable, noop, noopUnobservable, noopUnsubscribable };
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@guanghechen/observable",
3
- "version": "6.0.3",
3
+ "version": "6.1.1",
4
4
  "author": {
5
5
  "name": "guanghechen",
6
6
  "url": "https://github.com/guanghechen/"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.0.2",
10
+ "url": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.1.0",
11
11
  "directory": "packages/observable"
12
12
  },
13
- "homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.0.2/packages/observable#readme",
13
+ "homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.1.0/packages/observable#readme",
14
14
  "keywords": [
15
15
  "observable"
16
16
  ],
@@ -35,8 +35,7 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@guanghechen/disposable": "^1.0.0-alpha.4",
38
- "@guanghechen/error.types": "^1.0.1",
39
- "@guanghechen/observable.types": "^6.0.1"
38
+ "@guanghechen/subscriber": "^1.0.0-alpha.1"
40
39
  },
41
- "gitHead": "edc5a1b997fac81f96f1ba03200a13b829b3e91d"
40
+ "gitHead": "27fe762fadd81c1d1358194216264a3929dc3b47"
42
41
  }