Skip to main content

Yield усередині стрілкової функції

yield не можна використовувати всередині стрілкової функції. Оператор yield дозволений тільки в тілі функції-генератора (function* чи async function*), а стрілковий синтаксис генераторної форми не передбачає, тому спроба написати такий код закінчується синтаксичною помилкою.

Теорія

TL;DR

  • yield валідний лише всередині function* або async function*.
  • Стрілкова функція не може бути генератором: конструкцій *() => {} чи async* () => {} у стандарті ECMAScript немає.
  • Помилка виникає на етапі парсингу, тобто весь файл або модуль не виконається взагалі.
  • Стрілку всередині генератора використовувати можна, доки вона сама не містить yield.
  • Потрібна лінива послідовність, беріть function*; потрібна асинхронна послідовність, беріть async function*.

Швидкий приклад

javascript
// 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 як оператор.

Як це виглядає на практиці

javascript
const gen = () => { yield 1; // SyntaxError };

Помилка з'являється до виконання коду, під час розбору джерела. Точний текст залежить від рушія: V8 повідомляє щось на кшталт SyntaxError: Unexpected number, інші рушії формулюють інакше, але результат один, скрипт не запускається.

Додатковий нюанс: поза генератором у нестрогому режимі yield є звичайним ідентифікатором, тому рушій спершу читає його як ім'я змінної й лише потім спотикається об наступний токен. У строгому режимі та в ES-модулях yield є зарезервованим словом, і повідомлення буде іншим. Плутати ці повідомлення не варто: причина завжди та сама.

Правильний спосіб: function*

Якщо потрібен yield, оголошуйте генератор явно:

javascript
// 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, якого у стрілки просто немає.

Стрілки всередині генератора

Заборона стосується тіла самої стрілки, а не сусідства з генератором. Усередині генератора стрілки працюють як завжди:

javascript
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.
javascript
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 = () => * {}. Обидва варіанти невалідні.
  • Думати, що помилка виникає під час виклику. Насправді вона виникає під час парсингу, тому падає весь модуль, а не одна функція.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.