О генераторах в JavaScript ES6, и о том, почему изучать их необязательно

в 9:43, , рубрики: javascript, Блог компании RUVDS.com, разработка, Разработка веб-сайтов

С ростом популярности конструкции async/await растёт и интерес к её внутренним механизмам. Порывшись в интернете, несложно выяснить, что в основе async/await лежат широко известные промисы, и генераторы, которые пользуются куда меньшей известностью и популярностью.

О генераторах в JavaScript ES6, и о том, почему изучать их необязательно - 1

Материал, перевод которого мы сегодня публикуем, посвящён генераторам. А именно, тут мы поговорим о том, как они работают, и о том, как они, совместно с промисами, используются в недрах конструкции async/await. Автор этой статьи говорит, что генераторы, ради их практического применения, осваивать необязательно. Кроме того, он отмечает, что он рассчитывает на то, что читатель немного разбирается в промисах.

Итераторы и генераторы

В JavaScript, начиная с выхода стандарта ES6, появилось несколько новых возможностей, которые направлены на упрощение работы с асинхронными потоками данных и коллекциями. В эту категорию попадают итераторы и генераторы.

Примечательной возможностью итераторов является то, что они предоставляют средства для доступа к элементам коллекций по одному за раз, и при этом позволяют отслеживать идентификатор текущего элемента.

function makeIterator(array) {
  var nextIndex = 0;
  console.log("nextIndex =>", nextIndex);

  return {
    next: function() {
      return nextIndex < array.length
        ? { value: array[nextIndex++], done: false }
        : { done: true };
    }
  };
}

var it = makeIterator(["simple", "iterator"]);

console.log(it.next()); // {value: 'simple, done: false}
console.log(it.next()); // {value: 'iterator, done: false}
console.log(it.next()); // {done: true}

Выше мы передаём функции makeIterator() небольшой массив, содержащий пару элементов, после чего проходимся по нему с помощью итератора, вызывая метод it.next(). Обратите внимание на комментарии, демонстрирующие получаемые с помощью итератора результаты.

Теперь поговорим о генераторах. Генераторы — это функции, которые работают как фабрики итераторов. Рассмотрим простой пример, а затем поговорим о двух механизмах, имеющих отношение к генераторам.

function* sample() {
  yield "simple";
  yield "generator";
}

var it = sample();

console.log(it.next()); // {value: 'simple, done: false}
console.log(it.next()); // {value: 'generator, done: false}
console.log(it.next()); // {value: undefined, done: true}

Обратите внимание на звёздочку в объявлении функции. Это указывает на то, что данная функция является генератором. Кроме того, взгляните на ключевое слово yield. Оно приостанавливает выполнение функции и возвращает некое значение. Собственно, эти две особенности и являются теми самыми двумя механизмами, о которых мы говорили выше:

  • Функция-генератор — это функция, объявленная с использованием звёздочки около ключевого слова function или около имени функции.
  • Итератор генератора создаётся, когда вызывают функцию-генератор.

В общем-то, вышеописанный пример демонстрирует работу фабричной функции, генерирующей итераторы.

Теперь, когда мы разобрались в основах, поговорим о более интересных вещах. Итераторы и генераторы могут обмениваться данными в двух направлениях. А именно, генераторы, с помощью ключевого слова yield, могут возвращать значения итераторам, однако и итераторы могут отправлять данные генераторам, используя метод iterator.next('someValue'). Вот как это выглядит.

function* favBeer() {
  const reply = yield "What is your favorite type of beer?";
  console.log(reply);
  if (reply !== "ipa") return "No soup for you!";
  return "OK, soup.";
}

{
  const it = favBeer();
  const q = it.next().value; // Итератор задаёт вопрос
  console.log(q);
  const a = it.next("lager").value; // Получен ответ на вопрос
  console.log(a);
}

// What is your favorite beer?
// lager
// No soup for you!

{
  const it = favBeer();
  const q = it.next().value; // Итератор задаёт вопрос
  console.log(q);
  const a = it.next("ipa").value; // получен ответ на вопрос
  console.log(a);
}

// What is your favorite been?
// ipa
// OK, soup.

Генераторы и промисы

Теперь мы можем поговорить о том, как генераторы и промисы формируют базу конструкции async/await. Представьте, что вместо того, чтобы возвращать, с помощью ключевого слова yield, некие значения, генератор возвращает промисы. При таком раскладе генератор можно обернуть в функцию, которая будет ожидать разрешения промиса и возвращать значение промиса генератору в методе .next(), как было показано в предыдущем примере. Существует популярная библиотека, co, которая выполняет именно такие действия. Выглядит это так:

co(function* doStuff(){
  var result - yield someAsyncMethod();
  var another = yield anotherAsyncFunction();
});

Итоги

По мнению автора этого материала JS-разработчикам нужно знать о том, как работают генераторы, лишь для того, чтобы понимать особенности внутреннего устройства конструкции async/await. А вот использовать их непосредственно в собственном коде не стоит. Генераторы вводят в JavaScript возможность приостанавливать выполнение функции и возвращаться к ней когда (и если) разработчик сочтёт это необходимым. До сих пор мы, работая с JS-функциями, ожидали, что они, будучи вызванными, просто выполняются от начала до конца. Возможность их приостанавливать — это уже что-то новое, но этот функционал удобно реализован в конструкции async/await.

С этим мнением, конечно, можно и поспорить. Например, один из аргументов в пользу генераторов, сводится к тому, что знание того, как они работают, полезно для отладки кода с async/await, так как внутри этой конструкции скрываются генераторы. Однако автор материала полагает, что это, всё же, нечто иное, нежели использование генераторов в собственном коде.

Уважаемые читатели! Что вы думаете о генераторах? Может быть, вы знаете какие-то варианты их использования, которые оправдывают их непосредственное применение в коде JS-проектов?

Царский промо-код для скидки в 10% на наши виртуальные сервера:

О генераторах в JavaScript ES6, и о том, почему изучать их необязательно - 2

Автор: ru_vds

Источник

Поделиться

* - обязательные к заполнению поля