Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading