Internals: Deterministic Microtask Engine
SoulState's reactivity is built on a Deterministic Microtask Scheduler. Unlike libraries that trigger notifications synchronously or rely purely on React's internal batching, SoulState orchestrates its own propagation cycle to guarantee consistency across all subscribers (standard listeners, computeds, and React components).
The Core Scheduler: scheduleTask
The entire propagation pipeline is deferred to the microtask queue using queueMicrotask. This ensures that multiple setState calls within the same execution block are batched into a single, atomic propagation event.
Implementation: src/core/scheduler.ts
let isScheduled = false;
let queue: (() => void)[] = [];
export function scheduleTask(task: () => void): void {
queue.push(task);
if (!isScheduled) {
isScheduled = true;
queueMicrotask(flushTasks);
}
}
function flushTasks(): void {
const currentQueue = queue;
queue = [];
isScheduled = false;
for (let i = 0; i < currentQueue.length; i++) {
currentQueue[i]();
}
}
The Runtime Propagation Cycle
When setState is called, the Runtime identifies changed keys but defers propagation.
1. Update Request
The Runtime requests an update. If a batch is already in progress, it simply records the changed keys.
// src/core/runtime.ts (simplified)
private requestUpdate(): void {
if (this.isBatching) return;
this.isBatching = true;
scheduleTask(() => {
this.processUpdate();
this.isBatching = false;
});
}
2. Batching Logic
Multiple setState calls are merged into one processUpdate call.
store.setState({ a: 1 }); // Schedules microtask
store.setState({ b: 2 }); // Merges into pending microtask
store.setState({ c: 3 }); // Merges into pending microtask
// --- End of synchronous execution ---
// Microtask runs: processUpdate() handles { a, b, c } in one pass.
Why Microtasks?
Avoidance of UI Tearing
By using microtasks, SoulState ensures that the state transition is "all-or-nothing" from the perspective of the browser's paint cycle. All listeners see the final state of the batch.
Glitch-Free Topological Order
Combined with the InvalidationGraph, microtask batching allows SoulState to calculate the exact topological level of every affected node before notifying them. This prevents "glitches" where a subscriber might see an inconsistent intermediate state.
Transaction Interaction
When a transaction is active (beginTransaction), the microtask scheduler is bypassed in favor of the TransactionEngine's internal buffer. Once commitTransaction is called, the accumulated state is applied, and a single microtask update is scheduled.
store.beginTransaction();
store.setState({ a: 1 });
store.setState({ b: 2 });
store.commitTransaction(); // Schedules ONE microtask for both updates
Comparison: Sync vs. Async
| Feature | Synchronous (RTK/Zustand default) | SoulState (Microtask) |
|---|---|---|
| Batching | Manual/React-only | Automatic & Global |
| Consistency | Can "tear" outside React | Always Consistent |
| Performance | High re-render frequency | Minimal re-renders |
| Execution Order | Immediate | Deterministic Post-Task |
Deterministic Ordering
SoulState guarantees that subscribers are notified in the order they were registered, but only after all computed dependencies have been topologically resolved.
Systems-Grade Stability
By decoupling state updates from propagation via the microtask engine, SoulState provides a stable execution environment that remains predictable even under heavy concurrent load.