Under The Hood
frontend solidjs SolidJS 1.x

createEffect: Synchronous vs Microtask Scheduling & Tracking Scopes

Last updated
Prerequisites:
createSignal and reactive reads
Understanding of effects vs derived values
  • solidjs
  • createEffect
  • scheduling
  • microtask
  • tracking
  • lifecycle

Read at your depth

The practical view

createEffect(() => { console.log(count()); }) runs the callback once initially, then re-runs it whenever any signal it reads changes. Effects are for side effects (logging, DOM APIs, subscriptions) — NOT for derived values (use createMemo for that). By default effects are scheduled: multiple signal changes in the same synchronous block trigger ONE effect run (batched), executed in a microtask. To run immediately and synchronously, pass the { name } or use createRenderEffect for pre-render effects. Reading signals inside the callback registers the dependency; reads outside (in helpers you call from the callback) are tracked too, as long as they happen synchronously during the callback.

The same idea in other frameworks

react equivalent
useEffect runs after commit, per render; Solid effects run on signal change (not re-render), with cleanup-before-rerun semantics.

Legacy vs modern

Side-effect in a memo vs side-effect in createEffect

Performing side effects in a derived computation is a bug (it may run zero or many times); effects are the designated place for side effects.

before → after
Side effect inside a memo
const x = createMemo(() => {
  analytics.track('view'); // ❌ OLD: side effect in a pure memo
  return count() * 2;
});
Side effect in createEffect
createEffect(() => {
  analytics.track('count', count()); // ✅ NEW: fires on every count change
});

Interview gotchas

Context

This is the core scheduling question — interviewers want the batching and microtask semantics, which determine observable behavior in real event handlers and async code.

The mechanical answer

Effects are queued on a scheduler rather than run synchronously per change. When a signal setter marks an effect dirty, the effect is pushed to a queue, and a flush is scheduled via queueMicrotask. If several signals change in the same synchronous block (e.g. three setCount calls in one click handler), the effect is queued once (deduplicated) and runs exactly once when the microtask flush executes — after the current task completes, before the next macrotask. This collapses bursty updates into a single effect run. The effect reads the signals during its run, so it observes the final values, not intermediate ones. Synchronous effects (createRenderEffect) opt out of this batching.

Trap

The naive answer is 'effects run immediately when a signal changes'. They are batched and deferred to a microtask. Another trap: assuming an effect sees every intermediate value of a signal changed in a loop — it sees only the last value, because the callback runs once after the synchronous burst. Candidates who explain the scheduler queue and microtask flush show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.