Skip to content

Commit

Permalink
refactor: move updateComputed and notifyEffect functions to global scope
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Jan 11, 2025
1 parent a77698d commit b1c20bd
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 112 deletions.
38 changes: 19 additions & 19 deletions src/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ export function computed<T>(getter: (cachedValue?: T) => T): Computed<T> {
return new Computed<T>(getter);
}

export function updateComputed(computed: Computed): boolean {
const prevSub = activeSub;
setActiveSub(computed);
startTrack(computed);
try {
const oldValue = computed.currentValue;
const newValue = computed.getter(oldValue);
if (oldValue !== newValue) {
computed.currentValue = newValue;
return true;
}
return false;
} finally {
setActiveSub(prevSub);
endTrack(computed);
}
}

export class Computed<T = any> implements Dependency, Subscriber, ISignal<T> {
currentValue: T | undefined = undefined;

Expand All @@ -30,7 +48,7 @@ export class Computed<T = any> implements Dependency, Subscriber, ISignal<T> {
flags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)
&& isDirty(this, flags)
) {
if (this.update()) {
if (updateComputed(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
Expand All @@ -44,22 +62,4 @@ export class Computed<T = any> implements Dependency, Subscriber, ISignal<T> {
}
return this.currentValue!;
}

update(): boolean {
const prevSub = activeSub;
setActiveSub(this);
startTrack(this);
try {
const oldValue = this.currentValue;
const newValue = this.getter(oldValue);
if (oldValue !== newValue) {
this.currentValue = newValue;
return true;
}
return false;
} finally {
setActiveSub(prevSub);
endTrack(this);
}
}
}
33 changes: 17 additions & 16 deletions src/effect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { activeEffectScope } from './effectScope.js';
import { activeEffectScope, EffectScope } from './effectScope.js';
import { endTrack, isDirty, link, runInnerEffects, startTrack } from './internal.js';
import { Dependency, Link, Subscriber, SubscriberFlags } from './system.js';

Expand All @@ -24,6 +24,22 @@ export function effect<T>(fn: () => T): Effect<T> {
return e;
}

export function notifyEffect(effect: Effect | EffectScope): void {
const flags = effect.flags;
if (
flags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)
&& isDirty(effect, flags)
&& effect instanceof Effect
) {
effect.run();
return;
}
if (flags & SubscriberFlags.InnerEffectsPending) {
effect.flags = flags & ~SubscriberFlags.InnerEffectsPending;
runInnerEffects(effect.deps!);
}
}

export class Effect<T = any> implements Subscriber, Dependency {
// Dependency
subs: Link | undefined = undefined;
Expand All @@ -44,21 +60,6 @@ export class Effect<T = any> implements Subscriber, Dependency {
}
}

notify(): void {
const flags = this.flags;
if (
flags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)
&& isDirty(this, flags)
) {
this.run();
return;
}
if (flags & SubscriberFlags.InnerEffectsPending) {
this.flags = flags & ~SubscriberFlags.InnerEffectsPending;
runInnerEffects(this.deps!);
}
}

run(): T {
const prevSub = activeSub;
setActiveSub(this);
Expand Down
10 changes: 1 addition & 9 deletions src/effectScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { endTrack, runInnerEffects, startTrack } from './internal.js';
import { endTrack, startTrack } from './internal.js';
import { Link, Subscriber, SubscriberFlags } from './system.js';

export let activeEffectScope: EffectScope | undefined = undefined;
Expand Down Expand Up @@ -27,14 +27,6 @@ export class EffectScope implements Subscriber {
depsTail: Link | undefined = undefined;
flags: SubscriberFlags = SubscriberFlags.None;

notify(): void {
const flags = this.flags;
if (flags & SubscriberFlags.InnerEffectsPending) {
this.flags = flags & ~SubscriberFlags.InnerEffectsPending;
runInnerEffects(this.deps!);
}
}

run<T>(fn: () => T): T {
const prevSub = activeEffectScope;
setActiveScope(this);
Expand Down
23 changes: 10 additions & 13 deletions src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
import { Computed, updateComputed } from './computed.js';
import { Effect, notifyEffect } from './effect.js';
import { EffectScope } from './effectScope.js';
import { createSystem, Dependency, Subscriber } from './system.js';
import { Computed } from './computed.js';
import { Effect } from './effect.js';

export function isComputed(sub: Subscriber & Dependency): sub is Computed {
return 'update' in sub;
return 'getter' in sub;
}

export function isEffect(sub: Subscriber & Dependency): sub is Effect {
return 'notify' in sub;
export function isEffect(sub: Subscriber): sub is Effect | EffectScope {
return 'run' in sub;
}

const {
drainQueuedEffects,
endTrack,
isDirty,
link,
propagate,
runInnerEffects,
drainQueuedEffects,
shallowPropagate,
startTrack,
} = createSystem({
isComputed,
isEffect,
updateComputed(computed) {
return computed.update();
},
notifyEffect(effect) {
effect.notify();
},
updateComputed,
notifyEffect,
});

export {
drainQueuedEffects,
endTrack,
isDirty,
link,
propagate,
runInnerEffects,
drainQueuedEffects,
shallowPropagate,
startTrack,
};
49 changes: 24 additions & 25 deletions src/unstable/asyncComputed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,42 @@ export function asyncComputed<T>(getter: (cachedValue?: T) => AsyncGenerator<Dep
return new AsyncComputed<T>(getter);
}

export async function updateAsyncComputed(computed: Computed | AsyncComputed): Promise<boolean> {
try {
startTrack(computed);
const oldValue = computed.currentValue;
const generator = computed.getter(oldValue);
let current = await generator.next();
while (!current.done) {
const dep = current.value;
link(dep, computed);
current = await generator.next();
}
const newValue = await current.value;
if (oldValue !== newValue) {
computed.currentValue = newValue;
return true;
}
return false;
} finally {
endTrack(computed);
}
}

export class AsyncComputed<T = any> extends Computed {

async get(): Promise<T> {
const flags = this.flags;
if (flags & SubscriberFlags.Dirty) {
if (await this.update()) {
if (await updateAsyncComputed(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
}
}
} else if (flags & SubscriberFlags.ToCheckDirty) {
if (await asyncCheckDirty(this.deps!)) {
if (await this.update()) {
if (await updateAsyncComputed(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
Expand All @@ -32,27 +54,4 @@ export class AsyncComputed<T = any> extends Computed {
}
return this.currentValue!;
}

// @ts-expect-error
async update(): Promise<boolean> {
try {
startTrack(this);
const oldValue = this.currentValue;
const generator = this.getter(oldValue);
let current = await generator.next();
while (!current.done) {
const dep = current.value;
link(dep, this);
current = await generator.next();
}
const newValue = await current.value;
if (oldValue !== newValue) {
this.currentValue = newValue;
return true;
}
return false;
} finally {
endTrack(this);
}
}
}
50 changes: 25 additions & 25 deletions src/unstable/asyncEffect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Effect } from '../effect.js';
import { Effect, notifyEffect } from '../effect.js';
import { endTrack, isEffect, link, startTrack } from '../internal.js';
import { Dependency, SubscriberFlags } from '../system.js';
import { asyncCheckDirty } from './asyncSystem.js';
Expand All @@ -9,34 +9,34 @@ export function asyncEffect<T>(fn: () => AsyncGenerator<Dependency, T>): AsyncEf
return e;
}

export class AsyncEffect<T = any> extends Effect {

async notify(): Promise<void> {
let flags = this.flags;
if (flags & SubscriberFlags.Dirty) {
this.run();
export async function notifyAsyncEffect(effect: AsyncEffect): Promise<void> {
let flags = effect.flags;
if (flags & SubscriberFlags.Dirty) {
effect.run();
return;
}
if (flags & SubscriberFlags.ToCheckDirty) {
if (await asyncCheckDirty(effect.deps!)) {
effect.run();
return;
} else {
effect.flags = flags &= ~SubscriberFlags.ToCheckDirty;
}
if (flags & SubscriberFlags.ToCheckDirty) {
if (await asyncCheckDirty(this.deps!)) {
this.run();
return;
} else {
this.flags = flags &= ~SubscriberFlags.ToCheckDirty;
}
if (flags & SubscriberFlags.InnerEffectsPending) {
effect.flags = flags & ~SubscriberFlags.InnerEffectsPending;
let link = effect.deps!;
do {
const dep = link.dep;
if ('flags' in dep && isEffect(dep)) {
notifyEffect(dep);
}
}
if (flags & SubscriberFlags.InnerEffectsPending) {
this.flags = flags & ~SubscriberFlags.InnerEffectsPending;
let link = this.deps!;
do {
const dep = link.dep;
if ('flags' in dep && isEffect(dep)) {
dep.notify();
}
link = link.nextDep!;
} while (link !== undefined);
}
link = link.nextDep!;
} while (link !== undefined);
}
}

export class AsyncEffect<T = any> extends Effect {

async run(): Promise<T> {
try {
Expand Down
7 changes: 4 additions & 3 deletions src/unstable/asyncSystem.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Computed } from '../computed';
import { isComputed, shallowPropagate } from '../internal';
import { Link, SubscriberFlags } from '../system';
import { updateAsyncComputed } from './asyncComputed';

export async function asyncCheckDirty(link: Link): Promise<boolean> {
let stack = 0;
Expand All @@ -13,7 +14,7 @@ export async function asyncCheckDirty(link: Link): Promise<boolean> {
if ('flags' in dep) {
const depFlags = dep.flags;
if (depFlags & SubscriberFlags.Dirty) {
if (isComputed(dep) && await dep.update()) {
if (isComputed(dep) && await updateAsyncComputed(dep)) {
const subs = dep.subs!;
if (subs.nextSub !== undefined) {
shallowPropagate(subs);
Expand All @@ -40,7 +41,7 @@ export async function asyncCheckDirty(link: Link): Promise<boolean> {
if (prevLink !== undefined) {
subSubs.prevSub = undefined;
if (dirty) {
if (await sub.update()) {
if (await updateAsyncComputed(sub)) {
shallowPropagate(sub.subs!);
sub = prevLink.sub as Computed;
continue;
Expand All @@ -50,7 +51,7 @@ export async function asyncCheckDirty(link: Link): Promise<boolean> {
}
} else {
if (dirty) {
if (await sub.update()) {
if (await updateAsyncComputed(sub)) {
sub = subSubs.sub as Computed;
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/computed.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from 'vitest';
import { Computed, SubscriberFlags } from '../src';
import { Computed, SubscriberFlags, updateComputed } from '../src';
import { computed, signal } from './api';
import { isDirty, shallowPropagate } from '../src/internal';

Expand All @@ -25,7 +25,7 @@ test('should custom computed support recursion', () => {
flags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)
&& isDirty(this, flags)
) {
if (this.update()) {
if (updateComputed(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
Expand Down

0 comments on commit b1c20bd

Please sign in to comment.