My understanding of JS generator functions was lacking. I hit a wall recently, so I studied and took notes.
yield vs yield*
yield
The yield keyword is used to pause and resume a generator function or legacy generator function.
The yield keyword is used to pause and resume a generator function (see function* or legacy generator functions).
yield*
The yield* expression is used to delegate to another generator or iterable object.
The yield* expression delegates to another generator or iterable object.
Example
function* sub() {
for (let i = 65; i < 70; i++) {
yield String.fromCharCode(i);
}
}
function* main() {
yield 'begin';
yield sub();
yield '---------';
yield* sub();
yield 'end';
return 'main end';
}
for (const i of main()) {
console.log(i);
}

From the console output we can see:
yieldandyield*are different.yield sub()returns the sub iterator, whileyield* sub()yields each item from the sub iterator.
I think of the asterisk as “delegate all”: yield* delegates each item of the expression, whereas yield delegates the iterator as a whole.
Also note: the main generator has a return value that wasn’t printed. That’s because for...of cannot access the final return value. If we rewrite it, we can retrieve it:
const res = main();
let value = null;
while (value !== undefined) {
value = res.next().value;
if (value !== undefined) {
console.log(value);
}
}

Usage in Redux‑Saga
In practice, I use generators most often in Redux‑Saga effects. In some cases,
yieldandyield*can appear to behave the same.The official docs’ explanation of the difference is brief — basically just noting they’re different:
You can use the builtin
yield*operator to compose multiple Sagas in a sequential way. This allows you to sequence your macro-tasks in a procedural style.You can use the built-in
yield*operator to compose multiple Sagas sequentially, which lets you arrange your macro-tasks in a simple procedural style.
My understanding
- In sagas, most side effects we orchestrate are async (macro‑tasks), so ordering matters.
- If we must ensure all async in effect A — including async inside effect B that A calls — runs in sequence, use
yield*. If A only delegates to a single B, thenyieldvsyield*often results in the same outcome.
- If we must ensure all async in effect A — including async inside effect B that A calls — runs in sequence, use
Example
With Promises
Promises were introduced to solve callback hell in async programming.

