Concurrent set execution sequencing in Zustand stores with async middleware
0 reputation · 11 Jan 2024, 04:35 UTC
0 reputation · 11 Jan 2024, 04:35 UTC
This question investigates the defined execution order when multiple set operations are issued concurrently within a Zustand store, particularly when async middleware such as persist is active. The goal is to clarify whether call sequencing is deterministic, and how shallow merge semantics interact with interleaved asynchronous side effects.
Relevant constraints include Zustand's default shallow merge behavior, which may overwrite nested state properties without Immer integration, and the store's closure-based subscription model that does not guarantee ordering across simultaneous dispatches. Version assumptions are based on Zustand v4.x, where middleware interception points remain an area of practical uncertainty.
set calls overlap within the same microtask?In Zustand v4.x, multiple set calls made synchronously (i.e., without awaiting) are processed in the exact order they appear. Each call triggers a shallow merge and immediately notifies subscribers before the next set begins. Async middleware such as persist does not reorder these state updates; it wraps the inner set, executes the merge synchronously, and then returns a promise that resolves after its asynchronous side‑effect (e.g., writing to storage) finishes in a later microtask. Therefore, the final store state reflects the synchronous call order, while any middleware side‑effects run after all synchronous sets have completed.
store.set synchronously: shallow merge → subscriber notification → return.set, runs the inner set synchronously, then returns a promise resolved in a later microtask after its async work.await a set (i.e., the middleware returns a promise), the next set will only start after that promise resolves, so the order of state updates follows promise resolution order.The deterministic ordering for synchronous calls stems from Zustand’s implementation: the store’s set method is a plain function that updates the internal state and then iterates over subscribers. No internal queuing or reordering occurs. Async middleware adds a layer that defers its own work to the microtask queue, but it does not interfere with the synchronous state update pipeline. Consequently, side‑effects from middleware may observe a state that already includes later synchronous set calls.
persist middleware.set (or a subscriber) to capture the order of state updates.onStorageWrite callback to capture when the async side‑effect runs.set actions back‑to‑back without awaiting.await new Promise(r=>setTimeout(r,0)) between the two sets; notice the second state log appears after the delay, confirming promise‑resolution ordering.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.