BTC
ETH
SOL
BNB
GOLD
XRP
DOGE
ADA
Back to home
Tech

Signals, the push-pull based algorithm

Signals drive reactivity in frameworks like SolidJS, Vue 3.3, and Preact.

Signals drive reactivity in frameworks like SolidJS, Vue 3.3, and Preact. They enable fine-grained updates that only recompute what’s necessary when data changes. This push-pull algorithm outperforms React’s virtual DOM reconciliation in benchmarks—SolidJS apps often run 2-10x faster on update-heavy UIs, per JS Framework Benchmark results from 2023.

Why does this matter? Traditional React batches changes but re-renders entire component trees, wasting CPU on unchanged DOM. Signals track exact dependencies, updating single DOM nodes or values. In a 10,000-row table with sorting and filtering, React might repaint everything; signals touch only affected cells. Developers build responsive apps without optimization hacks.

The Reactive Foundation

Signals inherit from reactive programming, formalized in the 1970s by researchers like Conal Elliott. Data flows through a dependency graph: change a source, and derived values propagate automatically. Early JS ports include Knockout.js in 2010 with observables and RxJS in 2012 for streams.

Picture a spreadsheet: edit cell A1, and formulas in B1 (=A1*2) and C1 (=B1+1) update instantly. Signals formalize this. Define x=10, y=2*x (20), z=y+1 (21). Mutate x to 15; y becomes 30, z 31—no manual propagation.

Production use proves reliability. SolidJS launched signals in 2018; Vue adopted them experimentally in 3.2 (2023), fully in 3.3. Millions of users rely on them daily without the bloat of full re-renders.

Push-Pull Mechanics

The core is a push-pull algorithm. “Pull” happens during computation: an effect or computed value reads signals, tracking dependencies via a stack or scheduler. “Push” follows: a signal mutation increments its version and notifies subscribers, scheduling re-evaluations.

A naive push-only model broadcasts to all subscribers on every change, risking cascades or stale reads. The source code shows this simplicity:

const signal = (initial) => {
  let value = initial;
  const subs = new Set();
  return {
    get value() { return value; },
    set value(v) {
      if (value === v) return;
      value = v;
      for (const fn of Array.from(subs)) fn(v);
    },
    subscribe(fn) {
      subs.add(fn);
      return () => subs.delete(fn);
    }
  };
};

This works for basics but scales poorly—O(n) notifications per update. Real implementations add pull: during effect execution, clear prior deps, read signals (capturing them), then subscribe lazily. On mutation:

  1. Signal updates value and version.
  2. Push to direct subscribers (computeds/effects).
  3. Scheduler batches invalidations, pulls fresh values only when needed.

SolidJS uses a global scheduler with microtasks. Effects run synchronously if top-level, batched otherwise. This minimizes DOM thrashing—critical for 60fps UIs.

Implementation Realities and Tradeoffs

Building signals reveals nuances. Track “dirty” flags: a computed marks dirty on dep change, re-evaluates on next read. Cyclic deps? Detect via version checks, throw errors.

Here’s a minimal pull-aware effect:

const effect = (fn) => {
  let deps = new Set();
  const run = () => {
    // Clear old deps
    for (const d of deps) d.subs.delete(run);
    deps.clear();
    
    // Pull: track new deps during exec
    const prevOwner = currentOwner;
    currentOwner = run;
    const result = fn();
    currentOwner = prevOwner;
    
    return result;
  };
  run(); // Initial run
  return run;
};

Globals like currentOwner manage the stack—signal reads check it to add deps. Skeptical note: this magic works until you hit edge cases like async mutations or server-side rendering. Vue’s signals require explicit reactive() wrappers; Solid mandates them everywhere.

Performance wins are real—Krausest’s benchmarks (krausest.github.io) clock Solid at 100k+ updates/sec vs. React’s 10k. But signals demand discipline: mutable outside signals breaks reactivity. Migration from React takes weeks for large codebases.

Bottom line: signals deliver spreadsheet-like reactivity to JS apps. They cut waste in dynamic UIs, from dashboards to games. If your app chokes on updates, profile first—signals fix the right problems.

April 4, 2026 · 3 min · 17 views · Source: Lobsters

Related