Inside a JavaScript generator, what value does a `yield` expression evaluate to, and what happens to the argument passed to the generator's very first `next()` call?
answer
- communication runs both ways
- yield is an expression, not a statement
- the resuming call supplies its value
- the first call has nowhere to deliver
- prime the generator, then send
basics
~20 sA yield expression evaluates to the argument of the next() call that resumes it, which is how a caller pushes data back into a running generator. The first next() has no suspended yield waiting for a value, so its argument is discarded.
solid answer
~40 s`yield` is an expression, not a statement, so `const x = yield 'ready'` does two things: `'ready'` travels out as the `value` of that `next()` result, and the generator suspends. When the caller later calls `gen.next(reply)`, `reply` becomes the value of that `yield` expression and `x` is bound to it. That is the two-way channel generators give you — values out through `yield`, values in through `next()`. The first `next()` is the exception: it merely starts the body, and there is no paused `yield` for its argument to become, so the argument is silently ignored. Code that expects `gen.next(seed)` to seed the first `yield` is a common bug; pass the seed as a parameter to the generator function instead.
code
javascript · 12 linesfunction* accumulate(start) {
let total = start;
while (true) {
const add = yield total; // out: total, in: the next addend
total += add;
}
}
const acc = accumulate(10);
console.log(acc.next('ignored').value); // 10 — argument discarded
console.log(acc.next(5).value); // 15
console.log(acc.next(2).value); // 17go deeper
Know that yield sends a value out to the caller and pauses the generator, and that next() is what resumes it. Recognising const x = yield v as valid syntax is enough at this stage.
Explain the inward direction concretely: the argument to the resuming next() becomes the value of the paused yield, and the very first next() argument is discarded because no yield is waiting. Show the prime-then-send driver loop.
Demonstrate that you design around it — seed via parameters, document the priming call, and treat the statement after a yield as not guaranteed because throw() and return() can resume the generator abnormally.
Own the interface question: a two-way generator is a protocol between a body and a driver, so decide deliberately whether that coupling belongs in your API or whether a plain producer with explicit state is easier for other teams to reason about.
## `yield` is an expression Most people first meet `yield` as an output-only construct — a `return` you can do repeatedly. It is more than that. `yield expr` is an **expression** whose own value is supplied later, by whoever resumes the generator. That makes a generator a coroutine: the caller and the body take turns, and each hand-off can carry a payload in either direction. ```javascript function* echo() { const first = yield 'ready'; const second = yield `got ${first}`; return `got ${second}`; } ``` The outward direction is familiar: `'ready'` becomes `value` in the result object. The inward direction is the part interviews probe: `first` is not defined by anything inside the generator. It is whatever the caller passes to the `next()` call that wakes the generator up from that particular `yield`. ## Driving it ```javascript const g = echo(); console.log(g.next('ignored').value); // 'ready' console.log(g.next('a').value); // 'got a' console.log(g.next('b').value); // 'got b' (done: true) ``` Read it as an alternating conversation. The first `next('ignored')` starts the body; the body runs to `yield 'ready'` and stops there, having produced `'ready'`. The generator has now *asked a question*. The second call, `next('a')`, answers it: the paused `yield 'ready'` expression evaluates to `'a'`, `first` is bound to `'a'`, execution continues to the next `yield`, and `` `got a` `` comes out. And so on. ## Why the first argument is dropped At the moment of the first `next()`, the generator is suspended **before** its first statement, not at a `yield`. There is no pending expression whose value the argument could become, so the specification simply discards it — no error, no warning. The failure mode is quiet: `gen.next(config)` looks like it seeds the generator, and the generator sees nothing. The fix is to seed through parameters, which are bound when the generator function is called: ```javascript function* accumulate(start) { let total = start; while (true) { const add = yield total; // out: running total, in: next addend total += add; } } const acc = accumulate(10); console.log(acc.next().value); // 10 — start the body, read the seed console.log(acc.next(5).value); // 15 console.log(acc.next(2).value); // 17 ``` Notice the shape of the driver loop: an unadorned `next()` to prime the generator, then meaningful arguments from the second call onward. "Prime it first" is the standard idiom precisely because of the dropped first argument. ## Precedence and parenthesisation Because `yield` is an expression, it takes part in expressions — and it binds very loosely, roughly like an assignment. `const x = yield a + b` yields the sum, not `(yield a) + b`. When you want the incoming value used in a larger expression, parenthesise it: `const doubled = (yield a) * 2`. Similarly, `yield` used as a function argument must be parenthesised — `f((yield x))` — and a bare `yield` with no operand is legal, meaning "suspend and produce `undefined`". ## Where the incoming value really comes from One more precision: `next(v)` is not the only way to resume. The other two resumption methods deliver a *completion* rather than a value — `throw()` makes the paused `yield` throw, and `return()` makes it behave as if a `return` statement ran there. So a `yield` expression has three possible fates: evaluate to the `next()` argument, throw, or never complete at all because the generator was closed. Code that stores `const x = yield …` and then does cleanup should keep that in mind and use `try`/`finally` rather than assuming the line after the `yield` always runs. ## When this actually matters Two-way `yield` is what lets a generator act as a small protocol: the body describes a sequence of requests ("give me the next chunk", "here is a partial result, send the next input") and a driver outside supplies the answers. It is the mechanism behind coroutine-style runners generally, and it is the reason `yield` is described as suspension with communication rather than as a fancy `return`. In everyday application code most generators only ever produce values and ignore the inward channel — which is exactly why an interviewer uses this question to separate "I have used generators" from "I know what they are".
- How would you seed a generator with an initial value, given the first next() argument is thrown away?Pass it as a parameter to the generator function itself: `gen(seed)`. Parameters are bound when the generator object is created, so the body can read the seed before its first `yield`. Reserve `next(value)` for answers to questions the generator has already asked by yielding.
- Does `const x = yield a + b` yield `a` or the sum?The sum. `yield` binds loosely, about as loosely as assignment, so the whole `a + b` is its operand. If you want the incoming value to take part in an expression you must parenthesise the yield itself, as in `const doubled = (yield a) * 2`.
- Apart from an argument to next(), how else can a paused yield expression complete?Two ways. `gen.throw(err)` makes the paused `yield` throw `err` inside the body, so a surrounding `try`/`catch` sees it. `gen.return(v)` makes it behave as though a `return v` executed at that point, running enclosing `finally` blocks. So the statement after a `yield` is not guaranteed to run.
saying these in an interview costs you the question
- Thinks yield only sends values out and never receives any
- Expects the first next() argument to reach the first yield
- Treats yield as a statement that has no value
- Confuses the next() argument with the generator function's parameters
- Assumes the line after a yield always executes