Yield усередині стрілкової функції
yield не можна використовувати всередині стрілкової функції. Оператор yield дозволений тільки в тілі функції-генератора (function* чи async function*), а стрілковий синтаксис генераторної форми не передбачає, тому спроба написати такий код закінчується синтаксичною помилкою.
Теорія
TL;DR
yieldвалідний лише всерединіfunction*абоasync function*.- Стрілкова функція не може бути генератором: конструкцій
*() => {}чиasync* () => {}у стандарті ECMAScript немає. - Помилка виникає на етапі парсингу, тобто весь файл або модуль не виконається взагалі.
- Стрілку всередині генератора використовувати можна, доки вона сама не містить
yield. - Потрібна лінива послідовність, беріть
function*; потрібна асинхронна послідовність, берітьasync function*.
Швидкий приклад
// Not allowed: an arrow function cannot be a generator
const badGen = () => {
yield 1; // SyntaxError
};
// Correct: a generator function
function* goodGen() {
yield 1;
yield 2;
}
console.log([...goodGen()]); // [1, 2]Чому стрілка не може бути генератором
Стрілкова функція є скороченим синтаксисом для звичайної функції, але зі свідомо урізаною семантикою: вона не має власних this, arguments, super і new.target, її не можна викликати через new, і вона не має внутрішнього слота, який потрібен генератору.
Генератор, натомість, це окремий тип функції. Під час виклику він не виконує тіло, а повертає об'єкт-ітератор і зберігає стан виконання між викликами next(). Саме цей механізм призупинення та відновлення і стоїть за yield.
У специфікації yield описаний як YieldExpression, і граматика дозволяє його тільки всередині GeneratorBody чи AsyncGeneratorBody. Тіло стрілки такою продукцією не є, тому парсер навіть не розглядає yield як оператор.
Як це виглядає на практиці
const gen = () => {
yield 1; // SyntaxError
};Помилка з'являється до виконання коду, під час розбору джерела. Точний текст залежить від рушія: V8 повідомляє щось на кшталт SyntaxError: Unexpected number, інші рушії формулюють інакше, але результат один, скрипт не запускається.
Додатковий нюанс: поза генератором у нестрогому режимі yield є звичайним ідентифікатором, тому рушій спершу читає його як ім'я змінної й лише потім спотикається об наступний токен. У строгому режимі та в ES-модулях yield є зарезервованим словом, і повідомлення буде іншим. Плутати ці повідомлення не варто: причина завжди та сама.
Правильний спосіб: function*
Якщо потрібен yield, оголошуйте генератор явно:
// Declaration form
function* gen() {
yield 1;
yield 2;
}
// Anonymous expression form
const gen2 = function* () {
yield 'A';
yield 'B';
};
// Method shorthand inside an object
const source = {
*items() {
yield 'first';
yield 'second';
},
};
console.log([...gen()]); // [1, 2]
console.log([...gen2()]); // ['A', 'B']
console.log([...source.items()]); // ['first', 'second']Зірочка може стояти з будь-якого боку від пробілу: function* gen() і function *gen() еквівалентні. Важливо лише, що вона належить ключовому слову function, якого у стрілки просто немає.
Стрілки всередині генератора
Заборона стосується тіла самої стрілки, а не сусідства з генератором. Усередині генератора стрілки працюють як завжди:
function* gen() {
const nums = [1, 2, 3];
yield nums.map(n => n * 2); // the arrow is a plain callback, no yield inside
}
console.log(gen().next().value); // [2, 4, 6]Тут стрілка є звичайною функцією і yield не використовує, тому все коректно. Зверніть увагу: yield не «просочується» у вкладені функції. Навіть звичайна function усередині генератора не може викликати yield зовнішнього генератора, бо кожна функція має власне тіло і власний контекст призупинення.
Альтернативи та підсумкова таблиця
Коли хочеться поведінки «як yield» саме у стрілковому стилі, зазвичай допомагає одне з трьох:
- обгорнути логіку в генератор
function*і повертати з нього готовий результат; - узяти
async/awaitдля асинхронного потоку значень; - зібрати ітератор вручну через
Symbol.iteratorабоSymbol.asyncIterator.
async function* asyncGen() {
yield await Promise.resolve('data');
}
// Not allowed: there is no async generator arrow in the language
// const asyncGen = async* () => { ... };| Питання | Відповідь |
|---|---|
Чи можна yield у стрілковій функції? | Ні |
| Чому? | yield дозволений тільки в тілі function* або async function* |
| Альтернатива | Оголосити function* чи async function* |
| Чи можна викликати стрілку всередині генератора? | Так, якщо вона сама не містить yield |
Чи існує стрілковий генератор *=>? | Ні, стандарт його не передбачає |
Типові помилки
- Вважати, що
*() => {}просто «ще не підтримується браузерами». Це не питання підтримки: такої граматики в специфікації немає і не планується. - Плутати
yieldзawait.awaitдоступний у будь-якій асинхронній функції, зокрема у стрілковійasync () => {}, аyieldприв'язаний саме до генератора. - Очікувати, що
yieldспрацює у вкладеній функції або колбеку всередині генератора. Кожна функція має власне тіло, тожyieldзовнішнього генератора там недоступний. - Ставити зірочку в стрілці:
const g = *() => {}абоconst g = () => * {}. Обидва варіанти невалідні. - Думати, що помилка виникає під час виклику. Насправді вона виникає під час парсингу, тому падає весь модуль, а не одна функція.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.