Skip to content

Commit 419f855

Browse files
feat: push-pull-push model 🚀 (#19)
1 parent 75a52d4 commit 419f855

File tree

2 files changed

+61
-31
lines changed

2 files changed

+61
-31
lines changed

‎src/computed.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { activeEffectScope, activeScopeTrackId } from './effectScope.js';
21
import { activeSub, activeTrackId, nextTrackId, setActiveSub } from './effect.js';
3-
import { checkDirty, endTrack, IComputed, Link, link, startTrack, SubscriberFlags } from './system.js';
2+
import { activeEffectScope, activeScopeTrackId } from './effectScope.js';
3+
import { checkDirty, endTrack, IComputed, Link, link, shallowPropagate, startTrack, SubscriberFlags } from './system.js';
44
import type { ISignal } from './types.js';
55

66
export function computed<T>(getter: (cachedValue?: T) => T): Computed<T> {
@@ -27,36 +27,46 @@ export class Computed<T = any> implements IComputed, ISignal<T> {
2727
get(): T {
2828
const flags = this.flags;
2929
if (flags & SubscriberFlags.Dirty) {
30-
this.update();
30+
if (this.update()) {
31+
const subs = this.subs;
32+
if (subs !== undefined) {
33+
shallowPropagate(subs);
34+
}
35+
}
3136
} else if (flags & SubscriberFlags.ToCheckDirty) {
3237
if (checkDirty(this.deps!)) {
33-
this.update();
38+
if (this.update()) {
39+
const subs = this.subs;
40+
if (subs !== undefined) {
41+
shallowPropagate(subs);
42+
}
43+
}
3444
} else {
3545
this.flags = flags & ~SubscriberFlags.ToCheckDirty;
3646
}
3747
}
38-
const currentValue = this.currentValue!;
3948
if (activeTrackId) {
4049
if (this.lastTrackedId !== activeTrackId) {
4150
this.lastTrackedId = activeTrackId;
42-
link(this, activeSub!).value = currentValue;
51+
link(this, activeSub!);
4352
}
4453
} else if (activeScopeTrackId) {
4554
if (this.lastTrackedId !== activeScopeTrackId) {
4655
this.lastTrackedId = activeScopeTrackId;
47-
link(this, activeEffectScope!).value = currentValue;
56+
link(this, activeEffectScope!);
4857
}
4958
}
50-
return currentValue;
59+
return this.currentValue!;
5160
}
5261

53-
update(): T {
62+
update(): boolean {
5463
const prevSub = activeSub;
5564
const prevTrackId = activeTrackId;
5665
setActiveSub(this, nextTrackId());
5766
startTrack(this);
67+
const oldValue = this.currentValue;
5868
try {
59-
return this.currentValue = this.getter(this.currentValue);
69+
return (this.currentValue = this.getter(oldValue)) !== oldValue;
6070
} finally {
6171
setActiveSub(prevSub, prevTrackId);
6272
endTrack(this);

‎src/system.ts

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ export interface IEffect extends Subscriber {
44
}
55

66
export interface IComputed extends Dependency, Subscriber {
7-
update(): any;
7+
update(): boolean;
88
}
99

1010
export interface Dependency {
11-
currentValue?: any;
1211
subs: Link | undefined;
1312
subsTail: Link | undefined;
1413
lastTrackedId?: number;
@@ -23,7 +22,6 @@ export interface Subscriber {
2322
export interface Link {
2423
dep: Dependency | IComputed | (Dependency & IEffect);
2524
sub: Subscriber | IComputed | (Dependency & IEffect) | IEffect;
26-
value: any;
2725
// Reuse to link prev stack in checkDirty
2826
// Reuse to link prev stack in propagate
2927
prevSub: Link | undefined;
@@ -96,7 +94,6 @@ function linkNewDep(dep: Dependency, sub: Subscriber, nextDep: Link | undefined,
9694
newLink = {
9795
dep,
9896
sub,
99-
value: undefined,
10097
nextDep,
10198
prevSub: undefined,
10299
nextSub: undefined,
@@ -227,7 +224,18 @@ export function propagate(subs: Link): void {
227224
}
228225
}
229226

230-
function isValidLink(subLink: Link, sub: Subscriber) {
227+
export function shallowPropagate(link: Link): void {
228+
do {
229+
const updateSub = link.sub;
230+
const updateSubFlags = updateSub.flags;
231+
if (!(updateSubFlags & (SubscriberFlags.Dirty | SubscriberFlags.Tracking))) {
232+
updateSub.flags = updateSubFlags | SubscriberFlags.Dirty;
233+
}
234+
link = link.nextSub!;
235+
} while (link !== undefined);
236+
}
237+
238+
export function isValidLink(subLink: Link, sub: Subscriber): boolean {
231239
const depsTail = sub.depsTail;
232240
if (depsTail !== undefined) {
233241
let link = sub.deps!;
@@ -256,16 +264,21 @@ export function checkDirty(link: Link): boolean {
256264
if ('update' in dep) {
257265
const depFlags = dep.flags;
258266
if (depFlags & SubscriberFlags.Dirty) {
259-
if (dep.update() !== link.value) {
267+
if (dep.update()) {
268+
const subs = dep.subs!;
269+
if (subs.nextSub !== undefined) {
270+
shallowPropagate(subs);
271+
}
260272
dirty = true;
261273
}
262274
} else if (depFlags & SubscriberFlags.ToCheckDirty) {
263-
dep.subs!.prevSub = link;
275+
const depSubs = dep.subs!;
276+
if (depSubs.nextSub !== undefined) {
277+
depSubs.prevSub = link;
278+
}
264279
link = dep.deps!;
265280
++stack;
266281
continue;
267-
} else if (dep.currentValue !== link.value) {
268-
dirty = true;
269282
}
270283
}
271284
if (dirty || (nextDep = link.nextDep) === undefined) {
@@ -274,20 +287,28 @@ export function checkDirty(link: Link): boolean {
274287
do {
275288
--stack;
276289
const subSubs = sub.subs!;
277-
const prevLink = subSubs.prevSub!;
278-
subSubs.prevSub = undefined;
279-
if (dirty) {
280-
if (sub.update() !== prevLink.value) {
281-
sub = prevLink.sub as IComputed;
282-
continue;
290+
let prevLink = subSubs.prevSub!;
291+
if (prevLink !== undefined) {
292+
subSubs.prevSub = undefined;
293+
if (dirty) {
294+
if (sub.update()) {
295+
shallowPropagate(sub.subs!);
296+
sub = prevLink.sub as IComputed;
297+
continue;
298+
}
299+
} else {
300+
sub.flags &= ~SubscriberFlags.ToCheckDirty;
283301
}
284302
} else {
285-
sub.flags &= ~SubscriberFlags.ToCheckDirty;
286-
if (sub.currentValue !== prevLink.value) {
287-
dirty = true;
288-
sub = prevLink.sub as IComputed;
289-
continue;
303+
if (dirty) {
304+
if (sub.update()) {
305+
sub = subSubs.sub as IComputed;
306+
continue;
307+
}
308+
} else {
309+
sub.flags &= ~SubscriberFlags.ToCheckDirty;
290310
}
311+
prevLink = subSubs;
291312
}
292313
link = prevLink.nextDep!;
293314
if (link !== undefined) {
@@ -350,7 +371,6 @@ function clearTrack(link: Link): void {
350371
link.dep = undefined;
351372
// @ts-expect-error
352373
link.sub = undefined;
353-
link.value = undefined;
354374
link.nextDep = linkPool;
355375
linkPool = link;
356376

0 commit comments

Comments
 (0)