async-reactivity 2.0.25 → 2.0.27
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/lib/computed.js +38 -126
- package/lib/computed.test.js +294 -269
- package/lib/effect.js +130 -0
- package/lib/effect.test.js +192 -0
- package/lib/listener.test.js +1 -1
- package/lib/ref.js +17 -6
- package/lib/watcher.js +9 -37
- package/package.json +6 -6
- package/src/computed.test.ts +304 -274
- package/src/computed.ts +40 -131
- package/src/effect.test.ts +246 -0
- package/src/effect.ts +149 -0
- package/src/listener.test.ts +1 -1
- package/src/ref.ts +21 -6
- package/src/tsconfig.json +3 -3
- package/src/watcher.ts +9 -43
- package/types/computed.d.ts +7 -19
- package/types/computed.d.ts.map +1 -1
- package/types/effect.d.ts +27 -0
- package/types/effect.d.ts.map +1 -0
- package/types/effect.test.d.ts +2 -0
- package/types/effect.test.d.ts.map +1 -0
- package/types/ref.d.ts +7 -2
- package/types/ref.d.ts.map +1 -1
- package/types/watcher.d.ts +2 -9
- package/types/watcher.d.ts.map +1 -1
- package/lib/tracker.js +0 -18
- package/src/tracker.ts +0 -24
- package/types/tracker.d.ts +0 -10
- package/types/tracker.d.ts.map +0 -1
package/lib/computed.js
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import Tracker from "./tracker.js";
|
|
2
1
|
import defaultIsEqual from "./defaultIsEqual.js";
|
|
3
2
|
import { Deferrer } from "./deferrer.js";
|
|
4
3
|
import { debounce } from 'lodash-es';
|
|
5
|
-
|
|
6
|
-
(function (ComputedState) {
|
|
7
|
-
ComputedState[ComputedState["Invalid"] = 0] = "Invalid";
|
|
8
|
-
ComputedState[ComputedState["Valid"] = 1] = "Valid";
|
|
9
|
-
ComputedState[ComputedState["Uncertain"] = 2] = "Uncertain";
|
|
10
|
-
ComputedState[ComputedState["Computing"] = 3] = "Computing";
|
|
11
|
-
})(ComputedState || (ComputedState = {}));
|
|
4
|
+
import Effect, { EffectState, InSyncSymbol } from "./effect.js";
|
|
12
5
|
class CircularDependencyError extends Error {
|
|
13
6
|
}
|
|
14
|
-
export default class Computed extends
|
|
7
|
+
export default class Computed extends Effect {
|
|
8
|
+
_value;
|
|
15
9
|
getter;
|
|
16
10
|
isEqual;
|
|
17
|
-
|
|
18
|
-
dependencies = new Map(); // boolean indicates whether value is up to date in regards to the dependency (false - up to date, true - needs update)
|
|
19
|
-
computePromise;
|
|
20
|
-
computePromiseActions;
|
|
21
|
-
lastComputeAttemptPromise;
|
|
11
|
+
dependents = new Set();
|
|
22
12
|
deferrer;
|
|
23
|
-
abortController;
|
|
24
13
|
constructor(getter, isEqual = (defaultIsEqual), timeToLive) {
|
|
25
|
-
super()
|
|
14
|
+
super(((value, _firstRun, abortSignal) => {
|
|
15
|
+
return getter(value, this._value, abortSignal);
|
|
16
|
+
}), true);
|
|
26
17
|
this.getter = getter;
|
|
27
18
|
this.isEqual = isEqual;
|
|
28
|
-
this.prepareComputePromise();
|
|
29
19
|
if (timeToLive !== undefined) {
|
|
30
20
|
this.deferrer = new Deferrer(debounce(() => {
|
|
31
21
|
if (this.dependents.size === 0) {
|
|
@@ -34,137 +24,59 @@ export default class Computed extends Tracker {
|
|
|
34
24
|
}, timeToLive));
|
|
35
25
|
}
|
|
36
26
|
}
|
|
37
|
-
prepareComputePromise() {
|
|
38
|
-
this.computePromise = new Promise((resolve, reject) => {
|
|
39
|
-
this.computePromiseActions = {
|
|
40
|
-
resolve,
|
|
41
|
-
reject
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
27
|
get value() {
|
|
46
|
-
if (this.state ===
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const upToDate = ![...this.dependencies.entries()].some(([d, needsUpdate]) => {
|
|
54
|
-
if (needsUpdate && d instanceof Computed && d.state === ComputedState.Uncertain) {
|
|
55
|
-
d.value;
|
|
56
|
-
return this.dependencies.get(d);
|
|
28
|
+
if (this.state === EffectState.Initial || this.state === EffectState.Scheduled) {
|
|
29
|
+
const oldValue = this._value;
|
|
30
|
+
const newValue = this.run();
|
|
31
|
+
if (newValue !== InSyncSymbol) {
|
|
32
|
+
this._value = newValue;
|
|
33
|
+
if (this.isEqual(this._value, oldValue)) {
|
|
34
|
+
this.validateDependents();
|
|
57
35
|
}
|
|
58
|
-
return needsUpdate;
|
|
59
|
-
});
|
|
60
|
-
if (upToDate) {
|
|
61
|
-
this.finalizeComputing();
|
|
62
|
-
this.validateDependents();
|
|
63
|
-
return this._value;
|
|
64
36
|
}
|
|
65
37
|
}
|
|
66
|
-
else if (this.state === ComputedState.Computing) {
|
|
67
|
-
this.abortController?.abort();
|
|
68
|
-
}
|
|
69
|
-
this.state = ComputedState.Computing;
|
|
70
|
-
this.clearDependencies(true);
|
|
71
|
-
this.abortController = new AbortController();
|
|
72
|
-
const newValue = this.getter(this.trackDependency, this._value, this.abortController.signal);
|
|
73
|
-
if (this.isEqual(newValue, this._value)) {
|
|
74
|
-
this.handlePromiseThen(this.lastComputeAttemptPromise, this._value);
|
|
75
|
-
this.validateDependents();
|
|
76
|
-
}
|
|
77
|
-
else if (newValue instanceof Promise) {
|
|
78
|
-
const computeAttemptPromise = newValue
|
|
79
|
-
.then(result => this.handlePromiseThen(computeAttemptPromise, result))
|
|
80
|
-
.catch(error => this.handlePromiseCatch(computeAttemptPromise, error));
|
|
81
|
-
this.lastComputeAttemptPromise = computeAttemptPromise;
|
|
82
|
-
this._value = this.computePromise;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
this._value = newValue;
|
|
86
|
-
this.handlePromiseThen(this.lastComputeAttemptPromise, this._value);
|
|
87
|
-
}
|
|
88
38
|
return this._value;
|
|
89
39
|
}
|
|
90
|
-
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
this.dependencies.clear();
|
|
95
|
-
}
|
|
96
|
-
handlePromiseThen(computeAttemptPromise, result) {
|
|
97
|
-
if (this.lastComputeAttemptPromise === computeAttemptPromise) {
|
|
98
|
-
this.computePromiseActions.resolve(result);
|
|
99
|
-
this.finalizeComputing();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
handlePromiseCatch(computeAttemptPromise, error) {
|
|
103
|
-
if (this.lastComputeAttemptPromise === computeAttemptPromise) {
|
|
104
|
-
this.computePromiseActions.reject(error);
|
|
105
|
-
this.finalizeComputing();
|
|
40
|
+
validateDependents() {
|
|
41
|
+
for (const dependent of this.dependents.keys()) {
|
|
42
|
+
dependent.validate(this);
|
|
106
43
|
}
|
|
107
44
|
}
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
return undefined;
|
|
111
|
-
}
|
|
112
|
-
if (this.dependents.has(dependency)) {
|
|
45
|
+
addDependent(dependent) {
|
|
46
|
+
if (this.dependencies.has(dependent)) {
|
|
113
47
|
throw new CircularDependencyError();
|
|
114
48
|
}
|
|
115
|
-
this.
|
|
116
|
-
dependency.addDependent(this);
|
|
117
|
-
return dependency.value;
|
|
49
|
+
this.dependents.add(dependent);
|
|
118
50
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
this.lastComputeAttemptPromise = undefined;
|
|
123
|
-
this.abortController = undefined;
|
|
124
|
-
this.prepareComputePromise();
|
|
51
|
+
removeDependent(dependent, promise = Promise.resolve()) {
|
|
52
|
+
this.dependents.delete(dependent);
|
|
53
|
+
this.deferrer?.finally(promise);
|
|
125
54
|
}
|
|
126
55
|
invalidate(dependency) {
|
|
127
|
-
if (this.state ===
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
else if (this.state === ComputedState.Valid) {
|
|
132
|
-
this.state = ComputedState.Uncertain;
|
|
133
|
-
if (dependency) {
|
|
134
|
-
this.dependencies.set(dependency, true);
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
for (const dep of this.dependencies.keys()) {
|
|
138
|
-
this.dependencies.set(dep, true);
|
|
139
|
-
}
|
|
56
|
+
if (this.state === EffectState.Waiting) {
|
|
57
|
+
this.state = EffectState.Scheduled;
|
|
58
|
+
for (const dependent of this.dependents) {
|
|
59
|
+
dependent.invalidate(this);
|
|
140
60
|
}
|
|
141
|
-
super.invalidate();
|
|
142
61
|
}
|
|
62
|
+
super.invalidate(dependency);
|
|
143
63
|
}
|
|
144
64
|
forceInvalidate() {
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
this.state = ComputedState.Invalid;
|
|
65
|
+
if (this.dependencies.size === 0) {
|
|
66
|
+
this.invalidate();
|
|
148
67
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
68
|
+
else {
|
|
69
|
+
for (const dependency of this.dependencies.keys()) {
|
|
70
|
+
this.invalidate(dependency);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (this.state !== EffectState.Running) {
|
|
74
|
+
this.state = EffectState.Initial;
|
|
156
75
|
}
|
|
157
|
-
}
|
|
158
|
-
removeDependent(dependent, promise = Promise.resolve()) {
|
|
159
|
-
super.removeDependent(dependent);
|
|
160
|
-
this.deferrer?.finally(promise);
|
|
161
76
|
}
|
|
162
77
|
reset() {
|
|
163
|
-
this.clearDependencies(false);
|
|
164
|
-
this.state = ComputedState.Invalid;
|
|
165
78
|
this._value = undefined;
|
|
166
|
-
|
|
167
|
-
this.prepareComputePromise();
|
|
79
|
+
super.dispose();
|
|
168
80
|
}
|
|
169
81
|
dispose() {
|
|
170
82
|
this.reset();
|