Interactive lab
55 min
Computed Signals
Add lazy, cached createComputed that invalidates when dependencies change.
~/playground — 05-computed-signals · edit engine.js
Liveediting engine.js⌘↵ run tests
let activeEffect = null const effectCleanups = new WeakMap() const disposed = new WeakSet() let batchDepth = 0 const queuedEffects = new Set() let flushScheduled = false function flushEffects() { flushScheduled = false const toRun = [...queuedEffects] queuedEffects.clear() toRun.forEach((fn) => { if (!disposed.has(fn)) fn() }) } function scheduleEffect(fn) { if (disposed.has(fn)) return queuedEffects.add(fn) if (batchDepth > 0) return if (!flushScheduled) { flushScheduled = true queueMicrotask(flushEffects) } } function runEffect(effect, fn) { const prevCleanup = effectCleanups.get(effect) if (prevCleanup) prevCleanup() activeEffect = effect const cleanup = fn() activeEffect = null if (typeof cleanup === 'function') effectCleanups.set(effect, cleanup) else effectCleanups.delete(effect) } export function createSignal(initial) { let value = initial const subscribers = new Set() return { get() { if (activeEffect && !disposed.has(activeEffect)) subscribers.add(activeEffect) return value }, set(next) { value = next subscribers.forEach((fn) => scheduleEffect(fn)) }, subscribe(fn) { subscribers.add(fn) return () => subscribers.delete(fn) }, } } export function batch(fn) { batchDepth++ try { return fn() } finally { batchDepth-- if (batchDepth === 0 && queuedEffects.size && !flushScheduled) { flushScheduled = true queueMicrotask(flushEffects) } } } export function createEffect(fn) { const effect = () => { if (disposed.has(effect)) return runEffect(effect, fn) } effect() return effect } export function disposeEffect(effect) { if (disposed.has(effect)) return disposed.add(effect) queuedEffects.delete(effect) const cleanup = effectCleanups.get(effect) if (cleanup) cleanup() effectCleanups.delete(effect) } export function createComputed(fn) { // TODO: lazy cached computed with invalidation throw new Error('Not implemented') }
Preview goal: This lab is test-driven — verify caching and invalidation with Run tests.
Checkpoints
- ›Computed caches until dependency changes
- ›All Sandpack tests pass