Skip to main content

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

FeatureSynchronous (RTK/Zustand default)SoulState (Microtask)
BatchingManual/React-onlyAutomatic & Global
ConsistencyCan "tear" outside ReactAlways Consistent
PerformanceHigh re-render frequencyMinimal re-renders
Execution OrderImmediateDeterministic 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.