Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Yield усередині стрілкової функції». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Ні, `yield` не можна використовувати всередині стрілкової функції.** Граматика ECMAScript дозволяє `yield` лише в тілі функції-генератора, оголошеної через `function*` або `async function*`, а стрілковий синтаксис генераторної форми не має взагалі: ні `*() => {}`, ні `async* () => {}` у стандарті не існує. Тому такий код падає з `SyntaxError` ще на етапі парсингу, до того як виконається хоч один рядок. Якщо потрібна лінива послідовність значень, оголошуйте генератор через `function*`, а стрілку спокійно використовуйте всередині генератора як звичайний колбек, доки вона сама не містить `yield`. ```javascript const bad = () => { yield 1; }; // SyntaxError function* good() { yield 1; } // works ``` **Ключове:** `yield` прив'язаний до тіла генератора, а стрілкова функція генератором бути не може.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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 = () => * {}`. Обидва варіанти невалідні. - Думати, що помилка виникає під час виклику. Насправді вона виникає під час парсингу, тому падає весь модуль, а не одна функція.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.