Skip to content

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:

generator-example.js
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:

request-handler.js
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:

future-async-context.js
// Conceptual - this doesn't exist yet
const 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.

generator-context.js
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:

How it works
┌─────────────────────────────────────────────────────────┐
│ 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:

normal-async.jsx
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

Async context approaches
┌─────────────────┬────────────────────┬─────────────────────┐
│ 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

mistake-1.js
// WRONG: Thinking you MUST use generators
function* myAsyncComponent() {
const data = yield fetchData();
return data;
}
// CORRECT: Regular async works for most cases
async 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:

future-solidjs.js
// What we'll have eventually
const 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

  1. Generators in SolidJS 2.0 async examples are a workaround for missing native async context
  2. Use standard async/await with createAsync for most cases
  3. Generators only matter for context-tracking scenarios
  4. The TC39 AsyncContext proposal will make this obsolete
  5. 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