Why Does SolidJS 2.0 Use Generators in Async Examples? Understanding the Async Context Workaround
I was reading through the SolidJS 2.0 beta examples when I spotted something confusing:
function* loadData() { const data = yield fetch('/api/data'); return process(data);}Wait, generators? In async code? I thought we were done with this after async/await became standard.
The Confusion
I posted on Reddit: “Neat, but I don’t get the generator in their example. Did they just use it for the fun of it, or is there any requirement?”
The answer I got was surprising:
“The generator is only needed until JS gets this: https://github.com/tc39/proposal-async-context”
So generators are a workaround, not a requirement. Let me explain what’s actually going on.
The Problem: JavaScript’s Missing Async Context
Here’s what I didn’t understand at first. Consider this scenario:
async function handleRequest(userId) { const requestId = generateRequestId();
// Context: { requestId, userId }
await fetchUserData(userId); // Async boundary 1
// Are we still in the same context here? // How does logging know the requestId?
await updateUser(userId); // Async boundary 2
// What about here? logActivity(); // How does this know the original requestId?}The problem: JavaScript has no native way to track context across async boundaries.
When you cross an await, all your local variables are technically preserved, but implicit context—the kind that should flow through your call stack automatically—is lost.
Angular solved this with Zone.js, which patches every async API in JavaScript. But that’s heavy and invasive.
What We Need (But Don’t Have Yet)
The TC39 AsyncContext proposal will give us something like this:
// Conceptual - this doesn't exist yetconst requestContext = new AsyncContext.Map();
async function handleRequest(userId) { const requestId = generateRequestId();
await requestContext.run(requestId, async () => { // Context automatically propagated await fetchUserData(userId); await updateUser(userId); logActivity(); // Can access requestId via requestContext.get() });}
function logActivity() { const requestId = requestContext.get(); console.log(`Request ${requestId}: Activity logged`);}But until browsers implement this, frameworks need workarounds.
Why Generators Help
Generators provide something unique: explicit control flow that can capture context at creation time.
function* trackedAsyncOperation(context) { // Context captured here when generator is created const result1 = yield fetchFirstData(context); const result2 = yield fetchSecondData(context, result1); return combineResults(result1, result2);}The framework can wrap the generator and step through it, maintaining context across each yield:
┌─────────────────────────────────────────────────────────┐│ Generator Created ││ Context: { requestId: 'abc123', userId: 42 } │└─────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────┐│ yield fetchFirstData(context) ││ Framework pauses, preserves context │└─────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────┐│ Async operation completes ││ Framework resumes with context still available │└─────────────────────────────────────────────────────────┘This avoids global patching while still tracking context.
When You Actually Need Generators
Here’s the key insight: You probably don’t.
For most SolidJS 2.0 async code, this is what you should write:
import { createAsync } from "@solidjs/router";import { Suspense } from "solid-js";
function UserProfile({ userId }) { const user = createAsync(async () => { const response = await fetch(`/api/users/${userId}`); return response.json(); });
return ( <Suspense fallback={<Loading />}> <div>{user()?.name}</div> </Suspense> );}No generators. Just standard async/await.
Generators only appear in examples that demonstrate context tracking—scenarios where you need implicit context propagation across async boundaries.
Comparison
┌─────────────────┬────────────────────┬─────────────────────┐│ Approach │ Pros │ Cons │├─────────────────┼────────────────────┼─────────────────────┤│ Zone.js │ Automatic │ Heavy, patches APIs ││ (Angular) │ │ Performance cost │├─────────────────┼────────────────────┼─────────────────────┤│ Generators │ Explicit control │ Unusual syntax ││ (SolidJS 2.0) │ No global patching │ Learning curve │├─────────────────┼────────────────────┼─────────────────────┤│ AsyncContext │ Native, standard │ Not available yet ││ (Future) │ Best performance │ │└─────────────────┴────────────────────┴─────────────────────┘Common Mistakes
Mistake 1: Thinking generators are required for async in SolidJS 2.0
// WRONG: Thinking you MUST use generatorsfunction* myAsyncComponent() { const data = yield fetchData(); return data;}
// CORRECT: Regular async works for most casesasync function myAsyncComponent() { const data = await fetchData(); return data;}Mistake 2: Copying generator patterns without understanding
When you see a generator in an example, ask yourself:
- Is this demonstrating context tracking?
- Does my code need implicit context propagation?
- Could I use regular async/await with explicit parameters?
Mistake 3: Confusing createAsync with generators
createAsync in SolidJS works perfectly with regular async functions. Generators are only for specific context-tracking scenarios.
The Future
Once AsyncContext ships in browsers, SolidJS will likely deprecate the generator pattern. You’ll write:
// What we'll have eventuallyconst requestContext = new AsyncContext.Map();
async function handleRequest(userId) { await requestContext.run({ requestId, userId }, async () => { await fetchUserData(userId); await updateUser(userId); logActivity(); });}No generators. Clean, standard async/await with implicit context propagation.
Takeaways
- Generators in SolidJS 2.0 async examples are a workaround for missing native async context
- Use standard
async/awaitwithcreateAsyncfor most cases - Generators only matter for context-tracking scenarios
- The TC39
AsyncContextproposal will make this obsolete - When you see generators, ask: “Is this for context tracking?”
The generators aren’t there because SolidJS is behind the times—they’re there because JavaScript is. Once the language catches up, the workaround disappears.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments