# Exploration: What should AsyncContext do with generators?
The question of whether AsyncContext is preserved across yield is only rarely relevant. We need
1. to hold onto an iterator across multiple contexts, rather than iterating it and throwing it away in a single context
2. to be iterating the result of a generator, rather than a hand-implemented or simple collection iterator
## Example use cases
I looked for concrete real-world use cases where these conditions are satisfied: an iterator is iterated over in an incremental way (in potentially different contexts) and is typically specified with generator syntax.
I then considered which context makes the most sense. I keep in mind two different AsyncContext.Variable use cases: (1) tracing - where we hold a span reference in the context, and (2) cancellation - where we hold an AbortSignal. These are somewhat representative since they tend to want different kinds of context propagation.
### 1. Redux Saga
This redux middleware uses generators that yield an operation, e.g. "`call(api, args)`" and re-enters when that call completes, possibly in response to a promise resolving. It doesn't look like these operations include events, so changing context may not be very likely. The most interesting situation here is probably the `take` effect, which pauses the saga until a specific action is dispatched to the store. Here we might want to resume with the context that `put` that action, or else retain the same context from before the `take`. For tracing purposes, the dispatch context probably makes the most sense. For cancellation purposes, it could probably go either way. It's difficult to consider an alternative formulation without generators to see what migration from non-saga code might look like. One possibility is Redux Thunk, which uses async functions for a similar purpose - but these clearly do not preserve dispatch context. But since this is redux middleware, we're already in a framework-y situation.
### 2. Babylon.js coroutines
This game engine uses coroutines to script events over time without blocking the main render loop. A `yield` can be used to wait for the next frame, for example. The main game loop calls `next` on each active coroutine in the frame-advance logic. The pattern for yielding until a condition is satisfied is to yield a pending promise - it's unlikely the game loop would be able to call `next` in an appropriate dispatch context here (so tracing is likely already broken), though there are plenty of valid use cases where a coroutine might be waiting on something like user input. That said, it's at least as likely that the coroutine was kicked off in a certain context that would want to be retained. The cancellation use case seems more likely here, and would definitely want to preserve context across `yield`.
### 3. State machines
This is the classic example pointed to for why propagating dispatch context from `next()` into the `yield` is necessary - a server-side state machine managing a bunch of connections, where each multi-stage transaction might get its own generator invocation. To properly propagate trace information from incoming connections, we need to capture it through the `yield`. But cancellation still wants a connection-level variable here, so it’s still not a clear win.
## Other considerations
### yield*
`yield*` has been proposed to pass context directly to the delegated iterator. Together with initialization-time context, this leads to a difference in behavior between `yield*` and `for (const elem of iter) { yield elem; }`, which may be surprising. To reconcile this, we need to either make `yield*` not propagate the context, or make yield switch to dispatch context. Alternatively, there’s now precedent (via `Iterator.concat` IIRC) that these finer details needn’t be preserved, so this difference may be fine.
This compromise allows some contortions to use `yield*` instead of `yield` to at least get access to the dispatch context, but it's far from implicit/automatic. With `using` support, one could write `using void = yield* getNextContext(actualYieldValue);`. This is more awkward if we need to actually consume the argument passed to `next`: e.g. `using yieldResult = yield* getNextContext(); use(yieldResult.value);` or some such.
### Iterator helpers
It’s worth considering how the iterator helpers will behave. If I write `iter.map(mapper)`, and iterate over it across different contexts, will the `mapper` see the initial context from the `map` call (which is what we’d get if we used a generator) or will it see the individual contexts from each call to `next()`?