The function* keyword
The function* keyword creates a generator function, which does not run when you call it but instead returns a generator object (an iterator). That object lets you drive execution of the function by hand, through calls to .next(), .throw() and .return().
Theory
TL;DR
function*declares a generator; the asterisk goes right after the wordfunction.- Calling a generator returns an iterator, not the result of a computation.
yieldinside the body hands a value out and pauses execution until the next.next().- The generator object implements the iterator protocol, so it works in
for...of. next(value)passes a value back inside the function, into the result of theyieldexpression.async function*returns an async iterator, which you walk withfor await...of.
Quick example
function* gen() {
yield 1;
yield 2;
yield 3;
}
const iterator = gen(); // the function does not run immediately
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }- The first
gen()call returns an iterator, not a result. - Every
.next()runs execution up to the nextyield. - After the last
yieldthe function finishes and reportsdone: true.
Syntax
function* functionName(params) {
yield value1;
yield value2;
// ...
}- The asterisk (
*) after the wordfunctionmakes the function a generator. - The
yieldkeyword is used inside to pause execution and return an intermediate value.
function* name(), function *name() and function * name() are all equivalent, but the settled style is to keep the asterisk next to the word function.
How yield works
yield is a pause inside a generator function. It does three things:
- returns a value to the outside (the
valuefield); - pauses execution of the function;
- waits for the next
.next()call to continue.
function* greet() {
console.log('Start');
yield 'Hello';
console.log('Continuing');
yield 'How are you?';
}
const it = greet();
console.log(it.next().value); // "Hello"
console.log(it.next().value); // "How are you?"
console.log(it.next()); // done: trueBetween two yield statements execution really does stop, which is impossible to achieve with a regular function.
A generator is an iterator
The object returned by function* implements the iterator protocol, so you can pass it into for...of:
function* gen() {
yield 1;
yield 2;
}
for (const n of gen()) {
console.log(n);
}Output:
1
2Besides .next(), a generator object has two more methods: .return(value) finishes the generator early and yields { value, done: true }, while .throw(error) raises an error at the point of the current yield, so it can be caught inside the generator body with an ordinary try/catch.
How function* differs from a regular function
| Feature | function | function* |
|---|---|---|
| Returns | a result (value) | an iterator (generator object) |
| Can pause execution | No | Yes, through yield |
| Runs immediately | Yes | No, only on .next() |
| Used for | ordinary operations | lazy evaluation, iterators, streams |
| Can take values back in | No | Yes, through next(value) |
Passing values back in, and the async variant
The argument of next(value) lands inside the generator as the result of the yield expression where execution was paused.
function* conversation() {
const name = yield 'What is your name?';
yield `Hello, ${name}!`;
}
const chat = conversation();
console.log(chat.next().value); // "What is your name?"
console.log(chat.next('Maria').value); // "Hello, Maria!"The first next() starts the generator, the second passes 'Maria' back in, and that value lands in const name.
If you add async, you can use await inside the generator (ES2018):
async function* fetchChunks() {
yield await Promise.resolve('Chunk 1');
yield await Promise.resolve('Chunk 2');
}
for await (const chunk of fetchChunks()) {
console.log(chunk);
}Async generators return an async iterator and are walked with for await...of.
Summary:
| Property | Description |
|---|---|
function* | Declares a generator function |
| What it returns | An iterator (an object with .next(), .throw(), .return()) |
| Keyword used inside | yield |
| Distinctive trait | Code can be executed step by step and paused |
| Asynchronous variant | async function* |
| Main use | Iterators, lazy evaluation, data streams, complex asynchronous processes |
Common mistakes
- Expecting
gen()to return a value. The call returns a generator object; to get a value you need.next().value. - Forgetting the asterisk. Without
*the wordyieldin the body becomes a syntax error (in strict mode) or just an ordinary identifier. - Assuming the code before the first
yieldruns at call time. It only runs on the first.next(). - Trying to use an arrow function. Arrow functions cannot be generators, there is no
() => {}form with an asterisk; for object and class methods there is the shorthand*name() {}. - Ignoring
.return()and.throw(). These are the standard ways to finish a generator early or to inject an error into it, not exotica. - Iterating an
async function*with a plainfor...of. You needfor await...of, otherwise you get promises back.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.