signalium 0.2.2 → 0.2.4
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 +12 -0
- package/dist/config.d.ts +6 -2
- package/dist/config.js +11 -14
- package/dist/scheduling.d.ts +3 -1
- package/dist/scheduling.js +36 -26
- package/dist/signals.d.ts +8 -12
- package/dist/signals.js +103 -265
- package/dist/weakref.d.ts +2 -0
- package/dist/weakref.js +10 -0
- package/package.json +1 -1
- package/src/__tests__/async.test.ts +141 -11
- package/src/__tests__/subscription.test.ts +3 -3
- package/src/__tests__/utils/instrumented.ts +3 -3
- package/src/config.ts +14 -14
- package/src/scheduling.ts +42 -34
- package/src/signals.ts +113 -326
- package/src/weakref.ts +9 -0
package/src/signals.ts
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
-
import { scheduleDisconnect, scheduleWatcher } from './scheduling.js';
|
1
|
+
import { scheduleDirty, scheduleDisconnect, schedulePull, scheduleWatcher } from './scheduling.js';
|
2
|
+
import WeakRef from './weakref.js';
|
2
3
|
|
4
|
+
let CURRENT_ORD = 0;
|
3
5
|
let CURRENT_CONSUMER: ComputedSignal<any> | undefined;
|
4
|
-
let CURRENT_DEP_TAIL: Link | undefined;
|
5
|
-
let CURRENT_ORD: number = 0;
|
6
|
-
let CURRENT_IS_WATCHED: boolean = false;
|
7
6
|
let CURRENT_IS_WAITING: boolean = false;
|
8
|
-
let CURRENT_SEEN: WeakSet<ComputedSignal<any>> | undefined;
|
9
7
|
|
10
|
-
let
|
8
|
+
let ID = 0;
|
11
9
|
|
12
10
|
const enum SignalType {
|
13
11
|
Computed,
|
@@ -61,188 +59,30 @@ const enum SignalState {
|
|
61
59
|
const WAITING = Symbol();
|
62
60
|
|
63
61
|
interface Link {
|
64
|
-
id: number;
|
65
|
-
sub: WeakRef<ComputedSignal<any>>;
|
66
62
|
dep: ComputedSignal<any>;
|
63
|
+
sub: WeakRef<ComputedSignal<any>>;
|
67
64
|
ord: number;
|
68
65
|
version: number;
|
69
|
-
|
70
|
-
nextDep: Link | undefined;
|
71
|
-
nextSub: Link | undefined;
|
72
|
-
prevSub: Link | undefined;
|
66
|
+
consumedAt: number;
|
73
67
|
|
74
68
|
nextDirty: Link | undefined;
|
75
69
|
}
|
76
70
|
|
77
|
-
let linkPool: Link | undefined;
|
78
|
-
|
79
|
-
const checkForCircularLinks = (link: Link | undefined) => {
|
80
|
-
if (!link) return;
|
81
|
-
|
82
|
-
for (const key of ['nextDep', 'nextSub', 'prevSub', 'nextDirty'] as const) {
|
83
|
-
let currentLink: Link | undefined = link?.[key];
|
84
|
-
|
85
|
-
while (currentLink !== undefined) {
|
86
|
-
if (currentLink === link) {
|
87
|
-
throw new Error(
|
88
|
-
`Circular link detected via ${key}. This is a bug, please report it to the Signalium maintainers.`,
|
89
|
-
);
|
90
|
-
}
|
91
|
-
|
92
|
-
currentLink = currentLink[key];
|
93
|
-
}
|
94
|
-
}
|
95
|
-
};
|
96
|
-
|
97
|
-
const typeToString = (type: SignalType) => {
|
98
|
-
switch (type) {
|
99
|
-
case SignalType.Computed:
|
100
|
-
return 'Computed';
|
101
|
-
case SignalType.Subscription:
|
102
|
-
return 'Subscription';
|
103
|
-
case SignalType.Async:
|
104
|
-
return 'Async';
|
105
|
-
case SignalType.Watcher:
|
106
|
-
return 'Watcher';
|
107
|
-
}
|
108
|
-
};
|
109
|
-
|
110
|
-
const printComputed = (computed: ComputedSignal<any>) => {
|
111
|
-
const type = typeToString(computed._type);
|
112
|
-
|
113
|
-
return `ComputedSignal<${type}:${computed.id}>`;
|
114
|
-
};
|
115
|
-
|
116
|
-
const printLink = (link: Link) => {
|
117
|
-
const sub = link.sub.deref();
|
118
|
-
const subStr = sub === undefined ? 'undefined' : printComputed(sub);
|
119
|
-
const depStr = printComputed(link.dep);
|
120
|
-
|
121
|
-
return `Link<${link.id}> sub(${subStr}) -> dep(${depStr})`;
|
122
|
-
};
|
123
|
-
|
124
|
-
function linkNewDep(
|
125
|
-
dep: ComputedSignal<any>,
|
126
|
-
sub: ComputedSignal<any>,
|
127
|
-
nextDep: Link | undefined,
|
128
|
-
depsTail: Link | undefined,
|
129
|
-
ord: number,
|
130
|
-
): Link {
|
131
|
-
let newLink: Link;
|
132
|
-
|
133
|
-
if (linkPool !== undefined) {
|
134
|
-
newLink = linkPool;
|
135
|
-
linkPool = newLink.nextDep;
|
136
|
-
newLink.nextDep = nextDep;
|
137
|
-
newLink.dep = dep;
|
138
|
-
newLink.sub = sub._ref;
|
139
|
-
newLink.ord = ord;
|
140
|
-
} else {
|
141
|
-
newLink = {
|
142
|
-
id: id++,
|
143
|
-
dep,
|
144
|
-
sub: sub._ref,
|
145
|
-
ord,
|
146
|
-
version: 0,
|
147
|
-
nextDep,
|
148
|
-
nextDirty: undefined,
|
149
|
-
prevSub: undefined,
|
150
|
-
nextSub: undefined,
|
151
|
-
};
|
152
|
-
}
|
153
|
-
|
154
|
-
if (depsTail === undefined) {
|
155
|
-
sub._deps = newLink;
|
156
|
-
} else {
|
157
|
-
depsTail.nextDep = newLink;
|
158
|
-
}
|
159
|
-
|
160
|
-
if (dep._subs === undefined) {
|
161
|
-
dep._subs = newLink;
|
162
|
-
} else {
|
163
|
-
const oldTail = dep._subsTail!;
|
164
|
-
newLink.prevSub = oldTail;
|
165
|
-
oldTail.nextSub = newLink;
|
166
|
-
}
|
167
|
-
|
168
|
-
dep._subsTail = newLink;
|
169
|
-
|
170
|
-
return newLink;
|
171
|
-
}
|
172
|
-
|
173
|
-
function poolLink(link: Link) {
|
174
|
-
const dep = link.dep;
|
175
|
-
const nextSub = link.nextSub;
|
176
|
-
const prevSub = link.prevSub;
|
177
|
-
|
178
|
-
if (nextSub !== undefined) {
|
179
|
-
nextSub.prevSub = prevSub;
|
180
|
-
link.nextSub = undefined;
|
181
|
-
} else {
|
182
|
-
dep._subsTail = prevSub;
|
183
|
-
}
|
184
|
-
|
185
|
-
if (prevSub !== undefined) {
|
186
|
-
prevSub.nextSub = nextSub;
|
187
|
-
link.prevSub = undefined;
|
188
|
-
} else {
|
189
|
-
dep._subs = nextSub;
|
190
|
-
}
|
191
|
-
|
192
|
-
// @ts-expect-error - override to pool the value
|
193
|
-
link.dep = undefined;
|
194
|
-
// @ts-expect-error - override to pool the value
|
195
|
-
link.sub = undefined;
|
196
|
-
link.nextDep = linkPool;
|
197
|
-
linkPool = link;
|
198
|
-
|
199
|
-
link.prevSub = undefined;
|
200
|
-
}
|
201
|
-
|
202
|
-
export function endTrack(sub: ComputedSignal<any>, shouldDisconnect: boolean): void {
|
203
|
-
if (CURRENT_DEP_TAIL !== undefined) {
|
204
|
-
if (CURRENT_DEP_TAIL.nextDep !== undefined) {
|
205
|
-
clearTrack(CURRENT_DEP_TAIL.nextDep, shouldDisconnect);
|
206
|
-
CURRENT_DEP_TAIL.nextDep = undefined;
|
207
|
-
}
|
208
|
-
} else if (sub._deps !== undefined) {
|
209
|
-
clearTrack(sub._deps, shouldDisconnect);
|
210
|
-
sub._deps = undefined;
|
211
|
-
}
|
212
|
-
}
|
213
|
-
|
214
|
-
function clearTrack(link: Link, shouldDisconnect: boolean): void {
|
215
|
-
do {
|
216
|
-
const nextDep = link.nextDep;
|
217
|
-
|
218
|
-
if (shouldDisconnect) {
|
219
|
-
scheduleDisconnect(link.dep);
|
220
|
-
}
|
221
|
-
|
222
|
-
poolLink(link);
|
223
|
-
|
224
|
-
link = nextDep!;
|
225
|
-
} while (link !== undefined);
|
226
|
-
}
|
227
|
-
|
228
71
|
export class ComputedSignal<T> {
|
229
|
-
|
72
|
+
_id = ID++;
|
230
73
|
_type: SignalType;
|
231
74
|
|
232
|
-
|
233
|
-
_subsTail: Link | undefined;
|
234
|
-
|
235
|
-
_deps: Link | undefined;
|
236
|
-
_dirtyDep: Link | undefined;
|
75
|
+
_deps = new Map<ComputedSignal<any>, Link>();
|
237
76
|
|
77
|
+
_dirtyDep: Link | undefined = undefined;
|
78
|
+
_subs = new Set<Link>();
|
238
79
|
_state: SignalState = SignalState.Dirty;
|
239
|
-
|
240
80
|
_version: number = 0;
|
241
|
-
|
242
|
-
_connectedCount: number;
|
243
|
-
|
81
|
+
_computedCount: number = 0;
|
82
|
+
_connectedCount: number = 0;
|
244
83
|
_currentValue: T | AsyncResult<T> | undefined;
|
245
84
|
_compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T> | undefined;
|
85
|
+
|
246
86
|
_equals: SignalEquals<T>;
|
247
87
|
_ref: WeakRef<ComputedSignal<T>> = new WeakRef(this);
|
248
88
|
|
@@ -286,7 +126,7 @@ export class ComputedSignal<T> {
|
|
286
126
|
|
287
127
|
if (value.isPending) {
|
288
128
|
const currentConsumer = CURRENT_CONSUMER;
|
289
|
-
ACTIVE_ASYNCS.get(this)?.finally(() => currentConsumer
|
129
|
+
ACTIVE_ASYNCS.get(this)?.finally(() => schedulePull(currentConsumer));
|
290
130
|
|
291
131
|
CURRENT_IS_WAITING = true;
|
292
132
|
throw WAITING;
|
@@ -300,45 +140,33 @@ export class ComputedSignal<T> {
|
|
300
140
|
}
|
301
141
|
|
302
142
|
get(): T | AsyncResult<T> {
|
303
|
-
|
143
|
+
if (CURRENT_CONSUMER !== undefined) {
|
144
|
+
const { _deps: deps, _computedCount: computedCount, _connectedCount: connectedCount } = CURRENT_CONSUMER;
|
145
|
+
const prevLink = deps.get(this);
|
304
146
|
|
305
|
-
if (CURRENT_CONSUMER !== undefined && this._type !== SignalType.Watcher && !CURRENT_SEEN!.has(this)) {
|
306
147
|
const ord = CURRENT_ORD++;
|
307
148
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
break;
|
329
|
-
}
|
330
|
-
|
331
|
-
newLink = newLink.nextDep;
|
149
|
+
this._check(!prevLink && connectedCount > 0);
|
150
|
+
|
151
|
+
if (prevLink === undefined) {
|
152
|
+
const newLink = {
|
153
|
+
dep: this,
|
154
|
+
sub: CURRENT_CONSUMER._ref,
|
155
|
+
ord,
|
156
|
+
version: this._version,
|
157
|
+
consumedAt: CURRENT_CONSUMER._computedCount,
|
158
|
+
nextDirty: undefined,
|
159
|
+
};
|
160
|
+
|
161
|
+
deps.set(this, newLink);
|
162
|
+
this._subs.add(newLink);
|
163
|
+
} else if (prevLink.consumedAt !== computedCount) {
|
164
|
+
prevLink.ord = ord;
|
165
|
+
prevLink.version = this._version;
|
166
|
+
prevLink.consumedAt = computedCount;
|
167
|
+
// prevLink.nextDirty = undefined;
|
168
|
+
this._subs.add(prevLink);
|
332
169
|
}
|
333
|
-
|
334
|
-
this._check(CURRENT_IS_WATCHED && !prevTracked);
|
335
|
-
|
336
|
-
CURRENT_DEP_TAIL = newLink ?? linkNewDep(this, CURRENT_CONSUMER, nextDep, CURRENT_DEP_TAIL, ord);
|
337
|
-
|
338
|
-
if (process.env.NODE_ENV !== 'production') checkForCircularLinks(CURRENT_DEP_TAIL);
|
339
|
-
|
340
|
-
CURRENT_DEP_TAIL.version = this._version;
|
341
|
-
CURRENT_SEEN!.add(this);
|
342
170
|
} else {
|
343
171
|
this._check();
|
344
172
|
}
|
@@ -347,6 +175,7 @@ export class ComputedSignal<T> {
|
|
347
175
|
}
|
348
176
|
|
349
177
|
_check(shouldWatch = false): number {
|
178
|
+
// COUNTS.checks++;
|
350
179
|
let state = this._state;
|
351
180
|
let connectedCount = this._connectedCount;
|
352
181
|
|
@@ -361,19 +190,11 @@ export class ComputedSignal<T> {
|
|
361
190
|
if (this._type === SignalType.Subscription) {
|
362
191
|
state = SignalState.Dirty;
|
363
192
|
} else {
|
364
|
-
|
365
|
-
|
366
|
-
if (process.env.NODE_ENV !== 'production') checkForCircularLinks(link);
|
367
|
-
|
368
|
-
while (link !== undefined) {
|
369
|
-
const dep = link.dep;
|
370
|
-
|
193
|
+
for (const [dep, link] of this._deps) {
|
371
194
|
if (link.version !== dep._check(true)) {
|
372
195
|
state = SignalState.Dirty;
|
373
196
|
break;
|
374
197
|
}
|
375
|
-
|
376
|
-
link = link.nextDep;
|
377
198
|
}
|
378
199
|
}
|
379
200
|
}
|
@@ -398,7 +219,7 @@ export class ComputedSignal<T> {
|
|
398
219
|
}
|
399
220
|
|
400
221
|
if (state === SignalState.Dirty) {
|
401
|
-
this._run(wasConnected,
|
222
|
+
this._run(wasConnected, shouldConnect);
|
402
223
|
} else {
|
403
224
|
this._resetDirty();
|
404
225
|
}
|
@@ -409,22 +230,16 @@ export class ComputedSignal<T> {
|
|
409
230
|
return this._version;
|
410
231
|
}
|
411
232
|
|
412
|
-
_run(wasConnected: boolean,
|
233
|
+
_run(wasConnected: boolean, shouldConnect: boolean) {
|
413
234
|
const { _type: type } = this;
|
414
235
|
|
415
236
|
const prevConsumer = CURRENT_CONSUMER;
|
416
|
-
const prevOrd = CURRENT_ORD;
|
417
|
-
const prevSeen = CURRENT_SEEN;
|
418
|
-
const prevDepTail = CURRENT_DEP_TAIL;
|
419
|
-
const prevIsWatched = CURRENT_IS_WATCHED;
|
420
237
|
|
421
238
|
try {
|
422
239
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
423
240
|
CURRENT_CONSUMER = this;
|
424
|
-
|
425
|
-
|
426
|
-
CURRENT_DEP_TAIL = undefined;
|
427
|
-
CURRENT_IS_WATCHED = isConnected;
|
241
|
+
|
242
|
+
this._computedCount++;
|
428
243
|
|
429
244
|
switch (type) {
|
430
245
|
case SignalType.Computed: {
|
@@ -491,7 +306,7 @@ export class ComputedSignal<T> {
|
|
491
306
|
value.isSuccess = true;
|
492
307
|
|
493
308
|
this._version++;
|
494
|
-
this
|
309
|
+
scheduleDirty(this);
|
495
310
|
},
|
496
311
|
error => {
|
497
312
|
if (currentVersion !== this._version || error === WAITING) {
|
@@ -502,7 +317,7 @@ export class ComputedSignal<T> {
|
|
502
317
|
value.isPending = false;
|
503
318
|
value.isError = true;
|
504
319
|
this._version++;
|
505
|
-
this
|
320
|
+
scheduleDirty(this);
|
506
321
|
},
|
507
322
|
);
|
508
323
|
|
@@ -552,44 +367,37 @@ export class ComputedSignal<T> {
|
|
552
367
|
}
|
553
368
|
}
|
554
369
|
} finally {
|
555
|
-
|
370
|
+
const deps = this._deps;
|
371
|
+
|
372
|
+
for (const link of deps.values()) {
|
373
|
+
if (link.consumedAt === this._computedCount) continue;
|
374
|
+
|
375
|
+
const dep = link.dep;
|
376
|
+
|
377
|
+
if (wasConnected) {
|
378
|
+
scheduleDisconnect(dep);
|
379
|
+
}
|
380
|
+
|
381
|
+
deps.delete(dep);
|
382
|
+
dep._subs.delete(link);
|
383
|
+
}
|
556
384
|
|
557
385
|
CURRENT_CONSUMER = prevConsumer;
|
558
|
-
CURRENT_SEEN = prevSeen;
|
559
|
-
CURRENT_DEP_TAIL = prevDepTail;
|
560
|
-
CURRENT_ORD = prevOrd;
|
561
|
-
CURRENT_IS_WATCHED = prevIsWatched;
|
562
386
|
}
|
563
387
|
}
|
564
388
|
|
565
389
|
_resetDirty() {
|
566
390
|
let dirty = this._dirtyDep;
|
391
|
+
// COUNTS.dirtyResetIterations++;
|
567
392
|
|
568
393
|
while (dirty !== undefined) {
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
if (oldHead === undefined) {
|
573
|
-
dep._subs = dirty;
|
574
|
-
dirty.nextSub = undefined;
|
575
|
-
dirty.prevSub = undefined;
|
576
|
-
} else {
|
577
|
-
dirty.nextSub = oldHead;
|
578
|
-
dirty.prevSub = undefined;
|
579
|
-
oldHead.prevSub = dirty;
|
580
|
-
dep._subs = dirty;
|
581
|
-
}
|
582
|
-
|
583
|
-
if (process.env.NODE_ENV !== 'production') {
|
584
|
-
checkForCircularLinks(this._dirtyDep);
|
585
|
-
}
|
394
|
+
// COUNTS.dirtyResetIterations++;
|
395
|
+
dirty.dep._subs.add(dirty);
|
586
396
|
|
587
397
|
let nextDirty = dirty.nextDirty;
|
588
398
|
dirty.nextDirty = undefined;
|
589
399
|
dirty = nextDirty;
|
590
400
|
}
|
591
|
-
|
592
|
-
if (process.env.NODE_ENV !== 'production') checkForCircularLinks(this._dirtyDep);
|
593
401
|
}
|
594
402
|
|
595
403
|
_dirty() {
|
@@ -604,61 +412,43 @@ export class ComputedSignal<T> {
|
|
604
412
|
} else {
|
605
413
|
this._dirtyConsumers();
|
606
414
|
}
|
607
|
-
|
608
|
-
this._subs = undefined;
|
609
415
|
}
|
610
416
|
|
611
417
|
_dirtyConsumers() {
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
if (state === SignalState.MaybeDirty) {
|
635
|
-
let dirty = consumer._dirtyDep;
|
636
|
-
const ord = link.ord;
|
637
|
-
|
638
|
-
if (dirty!.ord > ord) {
|
639
|
-
consumer._dirtyDep = link;
|
640
|
-
link.nextDirty = dirty;
|
641
|
-
} else {
|
642
|
-
let nextDirty = dirty!.nextDirty;
|
643
|
-
|
644
|
-
while (nextDirty !== undefined && nextDirty!.ord < ord) {
|
645
|
-
dirty = nextDirty;
|
646
|
-
nextDirty = dirty.nextDirty;
|
418
|
+
for (const link of this._subs.values()) {
|
419
|
+
const sub = link.sub.deref();
|
420
|
+
|
421
|
+
if (sub === undefined) continue;
|
422
|
+
|
423
|
+
switch (sub._state) {
|
424
|
+
case SignalState.MaybeDirty: {
|
425
|
+
let dirty = sub._dirtyDep;
|
426
|
+
const ord = link.ord;
|
427
|
+
if (dirty!.ord > ord) {
|
428
|
+
sub._dirtyDep = link;
|
429
|
+
link.nextDirty = dirty;
|
430
|
+
} else {
|
431
|
+
let nextDirty = dirty!.nextDirty;
|
432
|
+
while (nextDirty !== undefined && nextDirty!.ord < ord) {
|
433
|
+
// COUNTS.dirtyInsertIterations++;
|
434
|
+
dirty = nextDirty;
|
435
|
+
nextDirty = dirty.nextDirty;
|
436
|
+
}
|
437
|
+
link.nextDirty = nextDirty;
|
438
|
+
dirty!.nextDirty = link;
|
647
439
|
}
|
648
|
-
|
649
|
-
|
650
|
-
|
440
|
+
break;
|
441
|
+
}
|
442
|
+
case SignalState.Clean: {
|
443
|
+
sub._state = SignalState.MaybeDirty;
|
444
|
+
sub._dirtyDep = link;
|
445
|
+
link.nextDirty = undefined;
|
446
|
+
sub._dirty();
|
651
447
|
}
|
652
|
-
} else {
|
653
|
-
// consumer._dirtyQueueLength = dirtyQueueLength + 2;
|
654
|
-
consumer._state = SignalState.MaybeDirty;
|
655
|
-
consumer._dirtyDep = link;
|
656
|
-
link.nextDirty = undefined;
|
657
|
-
consumer._dirty();
|
658
448
|
}
|
659
|
-
|
660
|
-
link = link.nextSub;
|
661
449
|
}
|
450
|
+
|
451
|
+
this._subs = new Set();
|
662
452
|
}
|
663
453
|
|
664
454
|
_disconnect(count = 1) {
|
@@ -679,14 +469,10 @@ export class ComputedSignal<T> {
|
|
679
469
|
}
|
680
470
|
}
|
681
471
|
|
682
|
-
|
683
|
-
|
684
|
-
while (link !== undefined) {
|
472
|
+
for (const link of this._deps.values()) {
|
685
473
|
const dep = link.dep;
|
686
474
|
|
687
475
|
dep._disconnect();
|
688
|
-
|
689
|
-
link = link.nextDep;
|
690
476
|
}
|
691
477
|
}
|
692
478
|
}
|
@@ -719,7 +505,7 @@ export interface AsyncReady<T> extends AsyncBaseResult<T> {
|
|
719
505
|
export type AsyncResult<T> = AsyncPending<T> | AsyncReady<T>;
|
720
506
|
|
721
507
|
class StateSignal<T> implements StateSignal<T> {
|
722
|
-
private
|
508
|
+
private _subs: WeakRef<ComputedSignal<unknown>>[] = [];
|
723
509
|
|
724
510
|
constructor(
|
725
511
|
private _value: T,
|
@@ -728,7 +514,7 @@ class StateSignal<T> implements StateSignal<T> {
|
|
728
514
|
|
729
515
|
get(): T {
|
730
516
|
if (CURRENT_CONSUMER !== undefined) {
|
731
|
-
this.
|
517
|
+
this._subs.push(CURRENT_CONSUMER._ref);
|
732
518
|
}
|
733
519
|
|
734
520
|
return this._value!;
|
@@ -740,21 +526,28 @@ class StateSignal<T> implements StateSignal<T> {
|
|
740
526
|
}
|
741
527
|
|
742
528
|
this._value = value;
|
529
|
+
const subs = this._subs;
|
530
|
+
const subsLength = subs.length;
|
743
531
|
|
744
|
-
|
745
|
-
|
746
|
-
for (const consumerRef of consumers) {
|
747
|
-
const consumer = consumerRef.deref();
|
532
|
+
for (let i = 0; i < subsLength; i++) {
|
533
|
+
const sub = subs[i].deref();
|
748
534
|
|
749
|
-
if (
|
535
|
+
if (sub === undefined) {
|
750
536
|
continue;
|
751
537
|
}
|
752
538
|
|
753
|
-
|
754
|
-
|
539
|
+
switch (sub._state) {
|
540
|
+
case SignalState.Clean:
|
541
|
+
sub._state = SignalState.Dirty;
|
542
|
+
sub._dirty();
|
543
|
+
break;
|
544
|
+
case SignalState.MaybeDirty:
|
545
|
+
sub._state = SignalState.Dirty;
|
546
|
+
break;
|
547
|
+
}
|
755
548
|
}
|
756
549
|
|
757
|
-
|
550
|
+
this._subs = [];
|
758
551
|
}
|
759
552
|
}
|
760
553
|
|
@@ -790,7 +583,7 @@ export interface Watcher {
|
|
790
583
|
}
|
791
584
|
|
792
585
|
export function watcher(fn: () => void): Watcher {
|
793
|
-
const subscribers
|
586
|
+
const subscribers: (() => void)[] = [];
|
794
587
|
const watcher = new ComputedSignal(SignalType.Watcher, () => {
|
795
588
|
fn();
|
796
589
|
|
@@ -809,10 +602,10 @@ export function watcher(fn: () => void): Watcher {
|
|
809
602
|
},
|
810
603
|
|
811
604
|
subscribe(subscriber: () => void) {
|
812
|
-
subscribers.
|
605
|
+
subscribers.push(subscriber);
|
813
606
|
|
814
607
|
return () => {
|
815
|
-
subscribers.
|
608
|
+
subscribers.splice(subscribers.indexOf(subscriber), 1);
|
816
609
|
};
|
817
610
|
},
|
818
611
|
};
|
@@ -824,19 +617,13 @@ export function isTracking(): boolean {
|
|
824
617
|
|
825
618
|
export function untrack<T = void>(fn: () => T): T {
|
826
619
|
const prevConsumer = CURRENT_CONSUMER;
|
827
|
-
const prevOrd = CURRENT_ORD;
|
828
|
-
const prevIsWatched = CURRENT_IS_WATCHED;
|
829
620
|
|
830
621
|
try {
|
831
622
|
CURRENT_CONSUMER = undefined;
|
832
623
|
// LAST_CONSUMED = undefined;
|
833
|
-
CURRENT_ORD = 0;
|
834
|
-
CURRENT_IS_WATCHED = false;
|
835
624
|
|
836
625
|
return fn();
|
837
626
|
} finally {
|
838
627
|
CURRENT_CONSUMER = prevConsumer;
|
839
|
-
CURRENT_ORD = prevOrd;
|
840
|
-
CURRENT_IS_WATCHED = prevIsWatched;
|
841
628
|
}
|
842
629
|
}
|
package/src/weakref.ts
ADDED