Ключове слово function*
Ключове слово function* створює функцію-генератор, яка під час виклику не виконується одразу, а повертає об'єкт-генератор (ітератор). Цей об'єкт дозволяє керувати виконанням функції вручну, за допомогою викликів .next(), .throw() і .return().
Теорія
TL;DR
function*оголошує генератор; зірочка стоїть одразу після словаfunction.- Виклик генератора повертає ітератор, а не результат обчислень.
yieldусередині тіла віддає значення назовні та призупиняє виконання до наступного.next().- Об'єкт-генератор реалізує протокол ітераторів, тому працює у
for...of. next(value)передає значення назад усередину функції, у результат виразуyield.async function*повертає асинхронний ітератор, який обходять черезfor await...of.
Швидкий приклад
function* gen() {
yield 1;
yield 2;
yield 3;
}
const iterator = gen(); // функція не виконується одразу
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 }- Перший виклик
gen()повертає ітератор, а не результат. - Кожен
.next()запускає виконання до наступногоyield. - Після останнього
yieldфункція завершується і віддаєdone: true.
Синтаксис
function* functionName(params) {
yield value1;
yield value2;
// ...
}- Зірочка (
*) після словаfunctionробить функцію генератором. - Ключове слово
yieldвикористовують усередині, щоб призупинити виконання і повернути проміжне значення.
Записи function* name(), function *name() і function * name() рівноцінні, але усталений стиль це зірочка біля слова function.
Як працює yield
yield це «пауза» у функції-генераторі. Він робить три речі:
- повертає значення назовні (поле
value); - призупиняє виконання функції;
- чекає наступного виклику
.next(), щоб продовжити.
function* greet() {
console.log('Початок');
yield 'Привіт';
console.log('Продовження');
yield 'Як справи?';
}
const it = greet();
console.log(it.next().value); // "Привіт"
console.log(it.next().value); // "Як справи?"
console.log(it.next()); // done: trueМіж двома yield виконання реально зупиняється, зі звичайною функцією такого досягти неможливо.
Генератор це ітератор
Об'єкт, який повертає function*, реалізує протокол ітераторів, тому його можна передати у for...of:
function* gen() {
yield 1;
yield 2;
}
for (const n of gen()) {
console.log(n);
}Вивід:
1
2Крім .next(), об'єкт-генератор має ще два методи: .return(value) достроково завершує генератор і віддає { value, done: true }, а .throw(error) кидає помилку в точці поточного yield, тож її можна перехопити всередині тіла генератора звичайним try/catch.
Чим function* відрізняється від звичайної функції
| Особливість | function | function* |
|---|---|---|
| Повертає | результат (value) | ітератор (generator object) |
| Можна призупиняти виконання | Ні | Так, через yield |
| Виконується одразу | Так | Ні, тільки на .next() |
| Використовують для | звичайних операцій | лінивих обчислень, ітераторів, потоків |
| Можна приймати значення назад | Ні | Так, через next(value) |
Передавання значень назад і асинхронний варіант
Аргумент next(value) потрапляє всередину генератора як результат того виразу yield, на якому виконання було зупинене.
function* conversation() {
const name = yield 'Як тебе звати?';
yield `Привіт, ${name}!`;
}
const chat = conversation();
console.log(chat.next().value); // "Як тебе звати?"
console.log(chat.next('Maria').value); // "Привіт, Maria!"Перший next() запускає генератор, другий передає 'Maria' назад, і це значення потрапляє в const name.
Якщо додати async, усередині генератора можна використовувати await (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);
}Асинхронні генератори повертають асинхронний ітератор, і перебирають їх через for await...of.
Підсумок:
| Властивість | Опис |
|---|---|
function* | Оголошує функцію-генератор |
| Що повертає | Ітератор (об'єкт з методами .next(), .throw(), .return()) |
| Ключове слово всередині | yield |
| Особливість | Код можна виконувати покроково і призупиняти |
| Асинхронний варіант | async function* |
| Основне застосування | Ітератори, ліниві обчислення, потоки даних, складні асинхронні процеси |
Типові помилки
- Очікувати, що
gen()поверне значення. Виклик повертає об'єкт-генератор; щоб отримати значення, потрібен.next().value. - Забути зірочку. Без
*словоyieldу тілі функції стає синтаксичною помилкою (у strict mode) або звичайним ідентифікатором. - Вважати, що код до першого
yieldвиконується під час виклику. Він виконається лише на першому.next(). - Використовувати стрілкову функцію. Стрілкові функції не можуть бути генераторами, форми
() => {}із зірочкою не існує; для методів об'єкта чи класу є скорочений запис*name() {}. - Ігнорувати
.return()і.throw(). Це штатні способи достроково завершити генератор або вкинути в нього помилку, а не екзотика. - Перебирати
async function*через звичайнийfor...of. Потрібенfor await...of, інакше ви отримаєте проміси.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.