From 1ddf2ceff9a18ab923e609ac0748690fa8006122 Mon Sep 17 00:00:00 2001 From: Nicolas Bayet Date: Wed, 1 Oct 2025 13:55:40 +0200 Subject: [PATCH] up --- src/common/types.ts | 13 ++-- src/runtime/executionContext.ts | 14 ++-- src/runtime/reactivity.ts | 124 +++++++++++++++++++------------- 3 files changed, 92 insertions(+), 59 deletions(-) diff --git a/src/common/types.ts b/src/common/types.ts index 02cb4ec2..e35da22b 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,9 +1,9 @@ export type ExecutionContext = { unsubcribe?: (scheduledContexts: Set) => void; update: Function; - signals: Set; - getParent: () => ExecutionContext | undefined; - getChildren: () => ExecutionContext[]; + atoms: Set; + // getParent: () => ExecutionContext | undefined; + // getChildren: () => ExecutionContext[]; meta: any; // schedule: () => void; }; @@ -13,6 +13,11 @@ export type customDirectives = Record< (node: Element, value: string, modifier: string[]) => void >; -export type Signal = { +export type Atom = { executionContexts: Set; + // dependents: Set; }; + +// export type DerivedAtom = Atom & { +// dependencies: Set; +// }; diff --git a/src/runtime/executionContext.ts b/src/runtime/executionContext.ts index 61dbdb17..5ad2f0ab 100644 --- a/src/runtime/executionContext.ts +++ b/src/runtime/executionContext.ts @@ -10,20 +10,20 @@ export function getExecutionContext() { export function makeExecutionContext({ update, - getParent, - getChildren, + // getParent, + // getChildren, meta, }: { update: () => void; - getParent?: () => ExecutionContext | undefined; - getChildren?: () => ExecutionContext[]; + // getParent?: () => ExecutionContext | undefined; + // getChildren?: () => ExecutionContext[]; meta?: any; }) { const executionContext: ExecutionContext = { update, - getParent: getParent!, - getChildren: getChildren!, - signals: new Set(), + // getParent: getParent!, + // getChildren: getChildren!, + atoms: new Set(), meta: meta || {}, }; return executionContext; diff --git a/src/runtime/reactivity.ts b/src/runtime/reactivity.ts index 367d3a68..ecffcd76 100644 --- a/src/runtime/reactivity.ts +++ b/src/runtime/reactivity.ts @@ -1,5 +1,5 @@ import { OwlError } from "../common/owl_error"; -import { ExecutionContext, Signal } from "../common/types"; +import { ExecutionContext, Atom } from "../common/types"; import { getExecutionContext, popExecutionContext, pushExecutionContext } from "./executionContext"; // Special key to subscribe to, to be notified of key creation/deletion @@ -77,14 +77,29 @@ export function toRaw>(value: U | T): T return targets.has(value) ? (targets.get(value) as T) : value; } -const targetToKeysToSignalItem = new WeakMap>(); -const scheduledSignals = new Set(); +const targetToKeysToAtomItem = new WeakMap>(); +const scheduledAtoms = new Set(); -function makeSignal() { - const signal: Signal = { +function makeAtom() { + const atom: Atom = { executionContexts: new Set(), + // dependents: new Set(), }; - return signal; + return atom; +} + +function getTargetKeyAtom(target: Target, key: PropertyKey): Atom { + let keyToAtomItem: Map = targetToKeysToAtomItem.get(target)!; + if (!keyToAtomItem) { + keyToAtomItem = new Map(); + targetToKeysToAtomItem.set(target, keyToAtomItem); + } + let atom = keyToAtomItem.get(key)!; + if (!atom) { + atom = makeAtom(); + keyToAtomItem.set(key, atom); + } + return atom; } /** @@ -100,44 +115,37 @@ function onReadTargetKey(target: Target, key: PropertyKey): void { const executionContext = getExecutionContext(); if (!executionContext) return; - let keyToSignalItem: Map = targetToKeysToSignalItem.get(target)!; - if (!keyToSignalItem) { - keyToSignalItem = new Map(); - targetToKeysToSignalItem.set(target, keyToSignalItem); - } - let signal = keyToSignalItem.get(key)!; - if (!signal) { - signal = makeSignal(); - keyToSignalItem.set(key, signal); - } - // observerSignals.add(signal); - executionContext.signals.add(signal); - signal.executionContexts.add(executionContext); + const atom = getTargetKeyAtom(target, key); + + // observerAtoms.add(atom); + executionContext.atoms.add(atom); + atom.executionContexts.add(executionContext); } let scheduled = false; -function scheduleSignal(signal: Signal) { - scheduledSignals.add(signal); +function scheduleAtom(atom: Atom) { + scheduledAtoms.add(atom); + // batched(processAtoms)(); if (scheduled) return; scheduled = true; Promise.resolve().then(() => { scheduled = false; - processSignals(); + processAtoms(); }); } -function processSignals() { +function processAtoms() { const scheduledContexts = new Set( - [...scheduledSignals.values()].map((s) => [...s.executionContexts]).flat() + [...scheduledAtoms.values()].map((s) => [...s.executionContexts]).flat() ); // schedule before context.update in case there is write operations during update // todo: add a test in case there is write operations during update the test - // will break is scheduledSignals.clear(); is called after context.update(); + // will break is scheduledAtoms.clear(); is called after context.update(); // that writes - scheduledSignals.clear(); + scheduledAtoms.clear(); for (const ctx of [...scheduledContexts]) { - removeSignalsFromContext(ctx); + removeAtomsFromContext(ctx); // custom unsubscribe depending on the context. // scheduledContexts might be updated while we're iterating over it. ctx.unsubcribe?.(scheduledContexts); @@ -173,15 +181,15 @@ function processSignals() { * or deleted) */ function onWriteTargetKey(target: Target, key: PropertyKey): void { - const keyToSignalItem = targetToKeysToSignalItem.get(target)!; - if (!keyToSignalItem) { + const keyToAtomItem = targetToKeysToAtomItem.get(target)!; + if (!keyToAtomItem) { return; } - const signal = keyToSignalItem.get(key); - if (!signal) { + const atom = keyToAtomItem.get(key); + if (!atom) { return; } - scheduleSignal(signal); + scheduleAtom(atom); } // Maps reactive objects to the underlying target @@ -240,31 +248,31 @@ export function reactive(target: T): T { return proxy; } -function removeSignalsFromContext(executionContext: ExecutionContext) { - for (const sig of executionContext.signals) { +function removeAtomsFromContext(executionContext: ExecutionContext) { + for (const sig of executionContext.atoms) { sig.executionContexts.delete(executionContext); } - executionContext.signals.clear(); + executionContext.atoms.clear(); } /** - * Unsubscribe an execution context and all its children from all signals + * Unsubscribe an execution context and all its children from all atoms * they are subscribed to. * - * @param executionContext the context to unsubscribe + * @param parentExecutionContext the context to unsubscribe */ function unsubscribeChildEffect( - executionContext: ExecutionContext, + parentExecutionContext: ExecutionContext, scheduledContexts: Set ) { // executionContext.update = () => {}; - for (const children of executionContext.meta.children) { + for (const children of parentExecutionContext.meta.children) { children.meta.parent = undefined; - removeSignalsFromContext(children); + removeAtomsFromContext(children); scheduledContexts.delete(children); unsubscribeChildEffect(children, scheduledContexts); } - executionContext.meta.children.length = 0; + parentExecutionContext.meta.children.length = 0; } export function withoutReactivity any>(fn: T): ReturnType { pushExecutionContext(undefined!); @@ -276,6 +284,7 @@ export function withoutReactivity any>(fn: T): Ret } return r; } + export function effect(fn: Function) { let parent = getExecutionContext(); // todo: is it useful? @@ -287,13 +296,13 @@ export function effect(fn: Function) { unsubscribeChildEffect(executionContext, scheduledContexts); }, update: fn, - getParent: () => { - return executionContext.meta.parent; - }, - getChildren: () => { - return executionContext.meta.children || []; - }, - signals: new Set(), + // getParent: () => { + // return executionContext.meta.parent; + // }, + // getChildren: () => { + // return executionContext.meta.children || []; + // }, + atoms: new Set(), meta: { parent: parent, children: [], @@ -311,6 +320,25 @@ export function effect(fn: Function) { } } +// const dependentStack: any[][] = []; + +// const derivedDependecies = new Set(); +// // const derrivedToAtom = new WeakMap(); +// export function derived(fn: Function) { +// const derivedAtom: DerivedAtom = { +// executionContexts: new Set(), +// // parent: null, +// dependencies: new Set(), +// dependents: new Set(), +// }; + +// return () => { +// dependentStack.push([]); +// fn(); +// dependentStack.pop(); +// }; +// } + /** * Creates a basic proxy handler for regular objects and arrays. *