Build a Reactivity Engine
Interactive lab

50 min

Batching and Scheduling

Batch multiple signal writes into a single microtask flush of effects.

~/playground — 04-batching-scheduling · edit engine.js

Live
let activeEffect = null
const effectCleanups = new WeakMap()
const disposed = new WeakSet()

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) => {
        if (!disposed.has(fn)) fn()
      })
    },
    subscribe(fn) {
      subscribers.add(fn)
      return () => subscribers.delete(fn)
    },
  }
}

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 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)
  const cleanup = effectCleanups.get(effect)
  if (cleanup) cleanup()
  effectCleanups.delete(effect)
}

Preview goal: Click +1 three times quickly — the preview should update smoothly (batched effects).

Checkpoints

  • Multiple sets inside batch produce one effect flush
  • All Sandpack tests pass