A Scheduler is the hidden commander of a reactivity system. It decides not whether something should run, but when it should run. New article: Building a Signal Scheduler
Signal Scheduler Architecture: Orchestrating Reactivity with Batching, Priorities, and Deferral
Current Situation Analysis
Reactivity systems often fail not because of poor state management, but because of uncontrolled execution models. When signals update, the default behavior in many naive implementations is immediate, synchronous propagation. This creates a "thundering herd" scenario where a single burst of mutations triggers cascading recalculations across the dependency graph. In complex applications, this results in redundant computations, excessive DOM updates, and race conditions that are notoriously difficult to debug.
The core oversight is treating reactivity as a simple pub/sub mechanism rather than a scheduling problem. Developers focus on what changes, ignoring when and in what order effects should run. Without a scheduler, the system cannot optimize for batched updates, prioritize critical UI renders over background calculations, or defer work until the result is actually consumed.
Data from performance profiling in reactive frameworks consistently shows that unbatched signal updates can increase computational overhead by 5x to 10x during high-frequency events (e.g., scroll handlers, rapid input, or WebSocket bursts). Furthermore, synchronous flushing blocks the main thread, causing frame drops and input latency. A robust scheduler transforms these spikes into manageable, optimized execution windows, reducing redundant work and preserving thread responsiveness.
WOW Moment: Key Findings
Implementing a structured scheduler fundamentally alters the performance characteristics of a reactive system. The following comparison illustrates the impact of moving from a naive synchronous model to an orchestrated scheduler with batching and priority queuing.
| Metric | Naive Synchronous Propagation | Orchestrated Scheduler | Improvement |
|---|---|---|---|
| Redundant Computations | O(N²) per burst | O(N) per burst | ~90% reduction |
| Main Thread Block Time | High (sync flush) | Near-zero (microtask/async) | Frame rate stability |
| Priority Handling | None (FIFO only) | High/Medium/Low tiers | Critical UI responsiveness |
| Lazy Evaluation | Eager (always runs) | Deferred until read | CPU savings on unused state |
| Batching Support | Manual/None | Automatic transaction scope | Developer ergonomics |
Why this matters: The scheduler acts as a traffic controller. By decoupling state mutation from effect execution, you gain the ability to coalesce updates, enforce execution order, and defer work. This enables features like automatic batching within event handlers, priority-based rendering for interactive elements, and lazy computation for expensive derived states. The result is a system that scales linearly with complexity rather than exponentially.
Core Solution
The architecture centers on three components: a Signal primitive that tracks dependencies, an ExecutionOrchestrator that manages the queue and flush cycles, and a Transaction mechanism for batching.
Architecture Decisions
- Priority Queue: Effects are not equal. UI updates must run before analytics logging. We implement a priority tier system to ensure critical tasks execute first.
- Microtask Flushing: Synchronous flushing blocks the thread. We use
queueMicrotaskto defer flushes, allowing the current execution context to complete and enabling automatic batching of multiple mutations. - Dirty Checking: To prevent infinite loops and redundant work, signals track a generation counter. Effects only re-run if their dependencies have changed since the last execution.
- Lazy Evaluation: Derived signals can be marked as lazy. They do not compute until their value is explicitly read, saving CPU cycles for states that may not be used in the current cycle.
Implementation
The following TypeScript implementa
🎉 Mid-Year Sale — Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all 635+ tutorials.
Sign In / Register — Start Free Trial7-day free trial · Cancel anytime · 30-day money-back
