Функция генератора JS и ключевое слово yield
Функция генератора и yield - это совершенно новая функция в JavaScript, предоставляемая EcmaScript 6.
В этой статье мы рассмотрим следующие темы:
- Что такое функция-генератор и ключевое слово yield
- Базовый пример
- Как остановить функцию генератора и не возвращать из нее все значения
- Выбрасывание исключения
- Передача аргументов в функцию .next ()
- Использование комбинации yield *
Что такое функция генератора
Синтаксис: функция генератора похожа на обычную функцию по синтаксису, за исключением того, что она содержит знак *, как в следующем примере:
function* generatorFn() {...}
В следующем примере все функции работают одинаково:
function* () {...} function *() {...} function* generatorFn() {...} function *generatorFn() {...}
Тип и конструктор:
- Функция генератора будет иметь тот же тип, что и обычная функция.
- Функция-генератор будет иметь прототип ‘GeneratorFunction’.
console.log(typeof function* generatorFn() {}); // "function" console.log((function*(){}).constructor.name); // "GeneratorFunction"
yield keyword: используется для приостановки и возобновления работы генератора.
Как это работает: в отличие от обычной функции-генератора функция возвращает несколько значений одно за другим, и при ее вызове выполняется только часть кода (до ключевого слова yield). Результатом оператора return будет объект-генератор, реализующий протоколы итераций.
Базовый пример
На мой взгляд, это всегда более понятно на живом примере, поэтому вот базовый пример:
let log = console.log; function* gen1() { yield 1; yield 2; yield 3; } let iterator = gen1(); log(iterator.next()); // { value: 1, done: false } log(iterator.next()); // { value: 1, done: false } log(iterator.next()); // { value: 1, done: false } log(iterator.next().done); // true
Как остановить функцию генератора и не возвращать из нее все значения
Чтобы остановить функцию генератора, нам нужно вызвать функцию .return (val). .return (val) принимает один аргумент, и этот аргумент будет возвращен как значение итерации.
let log = console.log; function* gen1() { yield 1; yield 2; yield 3; } let iterator = gen1(); log(iterator.next()); // 1 log(iterator.return(5)); // 5 log(iterator.next().done); // true
Выбрасывание исключения
Внутри функции генератора можно вручную сгенерировать исключение с помощью функции .throw (err). .throw (err) принимает один аргумент, описывающий ошибку. Вот пример:
let log = console.log; function* gen1() { try { yield 1; } catch(err) { console.log(`We got an error in ${err} try/catch block`); } try { yield 2; } catch(err) { console.log(`We got an error in ${err} try/catch block`); } } let iterator = gen1(); log(iterator.next()); log(iterator.throw('first')); log(iterator.throw('second').done); /* Result: ======= { value: 1, done: false } We got an error in undefined try/catch block { value: 2, done: false } We got an error in undefined try/catch block true */
Передача аргументов в функцию .next ()
Function .next () может принимать необязательный аргумент, и он будет использоваться в качестве результата yield, внимательно посмотрите на следующий пример:
function* gen() { console.log('0, start'); console.log(`1, ${yield}`); console.log(`2, ${yield}`); console.log(`3, ${yield}`); } let iterator = gen(); iterator.next(); // 0, start iterator.next('apple'); // 1 apple iterator.next('orange'); // 2 orange iterator.next('banana'); // 3 banana
и еще один пример:
let log = console.log; function* gen() { let a = yield 3; let b = yield a + 7; let c = yield b + 20; yield c + 1; } let iterator = gen(); log(iterator.next().value); // 3 log(iterator.next(12).value); // 19 log(iterator.next(11).value); // 31 log(iterator.next(15).value); // 16 log(iterator.next().done); // true
Объяснение (второй пример):
1. Мы вызываем `iterator.next (). Value`, он возвращает 3 в результате .next (). Value и будет приостановлен при первом yield, так что let a =… все еще не имеет значения
2. Мы вызываем iterator.next (12) .value, тогда сначала yield внутри функции .gen вернет 12 и будет присвоено переменной a, сразу после того, как yield a + 7 вернется на вершину 19 и будет приостановлено на втором уровне доходности, поэтому let b =… все еще не имеет значения.
3. Мы вызываем iterator.next (11) .value, тогда второй yield внутри функции .gen вернет 11 и будет назначен переменной b, сразу после того, как yield b + 20 вернется на вершину 31 и будет приостановлено на втором уровне доходности, поэтому let b =… все еще не имеет значения.
4. Мы вызываем iterator.next (15) .value, третий yield внутри функции .gen вернет 15 и будет назначен переменной c, сразу после того, как yield c + 1 вернется поверх 16.
5. Мы вызываем `iterator.next (). Done`, и он вернет true, потому что последний yield уже был подписан.
Использование комбинации yield *
Определение: выражение yield * используется для делегирования другому генератору или повторяемому объекту. Вот пример:
let log = console.log; function* gen1() { yield 2; yield 3; } function* gen2() { yield 1; yield* gen1(); yield 4; yield 5; } let iterator = gen2(); log(iterator.next().value); // 1 log(iterator.next().value); // 2 log(iterator.next().value); // 3 log(iterator.next().value); // 4 log(iterator.next().value); // 5 log(iterator.next().done); // true
Заключение
Спасибо, ребята, что прочитали. Надеюсь, вам понравилось, и вы узнали что-то новое, связанное с JavaScript. Пожалуйста, подпишитесь и нажмите кнопку «Хлопок», если вам понравилась эта статья.