Build1 publisher2 min readPublished
Solid 2.0's async createMemo forces the runtime to arbitrate between in-flight executions
A dev.to post walks through two fetches in flight for different user ids at once, and it shows why a reactive runtime needs an identity per execution before it can decide which result is still allowed to commit.
The Engineer · Build desk
What happened
- Solid 2.0's createMemo accepts an async callback, so a memo body can read a signal, await a fetch and return the parsed response.
- A dev.to post argues that admitting an async computation to the reactive graph means the runtime stops managing only cached values and starts managing executions.
- An async body spans time instead: it reads a dependency, starts a request, suspends, waits, resumes, and only then produces a result.
- In the post's walkthrough a userId change starts a second fetch while the first is still open, the second resolves first, and the first resolves afterwards holding the previous user.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Anyone adopting async memos is choosing a commit policy, because of the two completion orders for two in-flight executions, last-resolve-wins publishes the stale value in one.
- constraint The decision cannot be delegated to the Promise, so the runtime has to attach identity to each execution and re-check it at every resume point.
- exposure The failure mode is quiet: a value that looks internally consistent, drawn from a dependency the graph has already replaced.
- precedent Staleness rules settled at the graph level bound what the rendering layer can promise, since the author places Suspense and rendering policy downstream of the same distinction.
A revision counter is the cheapest structure that answers the question the post leaves hanging. Each execution is stamped with the graph revision current when it started: A gets 1, B gets 2 [13]. The numbering implies the test. Compare the stamp against the current revision when the execution resumes, and refuse the commit if the stamp is behind [16].
That comparison has to live in the runtime because Promise state cannot supply it. Pending, fulfilled and rejected are what a Promise exposes, and a fulfilled execution may already be stale, or superseded by a newer execution [12]. The author, publishing as luciano0322 on dev.to, wrote that a normal Promise "has no concept of I am outdated" and that "It only knows: I finished" [11].
The cost of skipping the check shows up in the walkthrough. userId is 1 and execution A starts a fetch for user 1; userId becomes 2 before A finishes, so the graph starts execution B for user 2; B resolves first, and A resolves later holding user 1 [10]. Two in-flight executions have two completion orders. In the A-then-B order, a last-resolve-wins policy publishes user 2 and is accidentally correct. In the B-then-A order, the same policy publishes user 1 over user 2, so one of the two orders is wrong and the network picks which one you get [15].
None of this exists synchronously. A single execution completes inside one call stack, so dependency tracking begins when the callback starts and the result is in hand when it returns [6]. The value lifecycle is mark stale, recompute, cache [5].
The signature hides all of it. At the type level the change looks like T becoming Promise<T> [9]. What the runtime now has to answer is which executions currently exist, which one is still valid, and which one is allowed to publish its result [8].
The post keeps to the graph. "I am not going to discuss UI or Suspense here," the author wrote [14], while noting that the same distinction reaches dependency tracking, scheduling, staleness, race conditions and eventually policies around Suspense and rendering [4]. The revision numbering is there to illustrate the commit question [17].
What to watch
- Whether Solid's 2.0 documentation states the commit rule for an async createMemo, and whether it matches the revision comparison this post sketches.
- Whether a superseded execution's request is cancelled or merely ignored, which the post does not reach.
- How the Suspense and rendering policy the author set aside treats an execution that is still in flight.