Skip to content

Commit

Permalink
feat: push-pull-push model
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Dec 22, 2024
1 parent 75a52d4 commit 678e29e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 31 deletions.
30 changes: 20 additions & 10 deletions src/computed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { activeEffectScope, activeScopeTrackId } from './effectScope.js';
import { activeSub, activeTrackId, nextTrackId, setActiveSub } from './effect.js';
import { checkDirty, endTrack, IComputed, Link, link, startTrack, SubscriberFlags } from './system.js';
import { activeEffectScope, activeScopeTrackId } from './effectScope.js';
import { checkDirty, endTrack, IComputed, Link, link, shallowPropagate, startTrack, SubscriberFlags } from './system.js';
import type { ISignal } from './types.js';

export function computed<T>(getter: (cachedValue?: T) => T): Computed<T> {
Expand All @@ -27,36 +27,46 @@ export class Computed<T = any> implements IComputed, ISignal<T> {
get(): T {
const flags = this.flags;
if (flags & SubscriberFlags.Dirty) {
this.update();
if (this.update()) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
}
}
} else if (flags & SubscriberFlags.ToCheckDirty) {
if (checkDirty(this.deps!)) {
this.update();
if (this.update()) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
}
}
} else {
this.flags = flags & ~SubscriberFlags.ToCheckDirty;
}
}
const currentValue = this.currentValue!;
if (activeTrackId) {
if (this.lastTrackedId !== activeTrackId) {
this.lastTrackedId = activeTrackId;
link(this, activeSub!).value = currentValue;
link(this, activeSub!);
}
} else if (activeScopeTrackId) {
if (this.lastTrackedId !== activeScopeTrackId) {
this.lastTrackedId = activeScopeTrackId;
link(this, activeEffectScope!).value = currentValue;
link(this, activeEffectScope!);
}
}
return currentValue;
return this.currentValue!;
}

update(): T {
update(): boolean {
const prevSub = activeSub;
const prevTrackId = activeTrackId;
setActiveSub(this, nextTrackId());
startTrack(this);
const oldValue = this.currentValue;
try {
return this.currentValue = this.getter(this.currentValue);
return (this.currentValue = this.getter(oldValue)) !== oldValue;
} finally {
setActiveSub(prevSub, prevTrackId);
endTrack(this);
Expand Down
62 changes: 41 additions & 21 deletions src/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ export interface IEffect extends Subscriber {
}

export interface IComputed extends Dependency, Subscriber {
update(): any;
update(): boolean;
}

export interface Dependency {
currentValue?: any;
subs: Link | undefined;
subsTail: Link | undefined;
lastTrackedId?: number;
Expand All @@ -23,7 +22,6 @@ export interface Subscriber {
export interface Link {
dep: Dependency | IComputed | (Dependency & IEffect);
sub: Subscriber | IComputed | (Dependency & IEffect) | IEffect;
value: any;
// Reuse to link prev stack in checkDirty
// Reuse to link prev stack in propagate
prevSub: Link | undefined;
Expand Down Expand Up @@ -96,7 +94,6 @@ function linkNewDep(dep: Dependency, sub: Subscriber, nextDep: Link | undefined,
newLink = {
dep,
sub,
value: undefined,
nextDep,
prevSub: undefined,
nextSub: undefined,
Expand Down Expand Up @@ -227,7 +224,18 @@ export function propagate(subs: Link): void {
}
}

function isValidLink(subLink: Link, sub: Subscriber) {
export function shallowPropagate(link: Link): void {
do {
const updateSub = link.sub;
const updateSubFlags = updateSub.flags;
if (!(updateSubFlags & (SubscriberFlags.Dirty | SubscriberFlags.Tracking))) {
updateSub.flags = updateSubFlags | SubscriberFlags.Dirty;
}
link = link.nextSub!;
} while (link !== undefined);
}

export function isValidLink(subLink: Link, sub: Subscriber): boolean {
const depsTail = sub.depsTail;
if (depsTail !== undefined) {
let link = sub.deps!;
Expand Down Expand Up @@ -256,16 +264,21 @@ export function checkDirty(link: Link): boolean {
if ('update' in dep) {
const depFlags = dep.flags;
if (depFlags & SubscriberFlags.Dirty) {
if (dep.update() !== link.value) {
if (dep.update()) {
const subs = dep.subs!;
if (subs.nextSub !== undefined) {
shallowPropagate(subs);
}
dirty = true;
}
} else if (depFlags & SubscriberFlags.ToCheckDirty) {
dep.subs!.prevSub = link;
const depSubs = dep.subs!;
if (depSubs.nextSub !== undefined) {
depSubs.prevSub = link;
}
link = dep.deps!;
++stack;
continue;
} else if (dep.currentValue !== link.value) {
dirty = true;
}
}
if (dirty || (nextDep = link.nextDep) === undefined) {
Expand All @@ -274,20 +287,28 @@ export function checkDirty(link: Link): boolean {
do {
--stack;
const subSubs = sub.subs!;
const prevLink = subSubs.prevSub!;
subSubs.prevSub = undefined;
if (dirty) {
if (sub.update() !== prevLink.value) {
sub = prevLink.sub as IComputed;
continue;
let prevLink = subSubs.prevSub!;
if (prevLink !== undefined) {
subSubs.prevSub = undefined;
if (dirty) {
if (sub.update()) {
shallowPropagate(sub.subs!);
sub = prevLink.sub as IComputed;
continue;
}
} else {
sub.flags &= ~SubscriberFlags.ToCheckDirty;
}
} else {
sub.flags &= ~SubscriberFlags.ToCheckDirty;
if (sub.currentValue !== prevLink.value) {
dirty = true;
sub = prevLink.sub as IComputed;
continue;
if (dirty) {
if (sub.update()) {
sub = subSubs.sub as IComputed;
continue;
}
} else {
sub.flags &= ~SubscriberFlags.ToCheckDirty;
}
prevLink = subSubs;
}
link = prevLink.nextDep!;
if (link !== undefined) {
Expand Down Expand Up @@ -350,7 +371,6 @@ function clearTrack(link: Link): void {
link.dep = undefined;
// @ts-expect-error
link.sub = undefined;
link.value = undefined;
link.nextDep = linkPool;
linkPool = link;

Expand Down

0 comments on commit 678e29e

Please sign in to comment.