Что такое генераторы ES6 и как их использовать в node.js?

Сегодня я был на узле.js meetup, и кто-то, с кем я там познакомился, сказал, что node.js имеет генераторы es6. Он сказал, что это огромный алгоритм impro.next () для программирования обратного вызова и изменит ландшафт узла. Айц, он сказал что-то о стеке вызовов и исключениях.

Я посмотрел на них, но на самом деле не нашел ни одного ресурса, который объяснял бы их дружелюбно. Каков общий обзор генераторов высокого уровня и как отличаются (или лучше?), Чем обратные вызовы?

PS: Было бы очень полезно, если бы вы могли дать фрагмент кода, чтобы подчеркнуть разницу в общих сценариях (создание HTTP-запроса или вызов db).

javascript,node.js,generator,ecmascript-6,

* 7

Ответов: 5


13

Генераторы, волокна и сопрограммы

«Генераторы» (помимо «генераторов») также являются основными блоками зданий «волокон» или «сопрограмм» . С помощью волокон вы можете «дать» функцию функции * seq () { var n = 0 ; while ( true ) yield n ++; } для вызова avar s = seq () для возврата, эффективно избегая объявления функции обратного вызова «на месте» и создания «закрытия». Попрощайтесь с адом.

Закрытие и попытка

... он сказал что-то о стеке вызовов и исключениях

Проблема с «закрытием» заключается в том, что даже если они «магически» сохраняют состояние локальных переменных для обратного вызова, «закрытие» не может содержать стек вызовов.

В момент обратного вызова, как правило, вызывающая функция вернулась давным-давно, поэтому любой блок «catch» на вызывающей функции не может перехватывать исключения в самой функции s.next () или обратном вызове . Это представляет большую проблему. Из-за этого вы не можете комбинировать обратные вызовы + замыкания с исключением catch.

Ждать

... и изменит ландшафт узла

Если вы используете генераторы для создания вспомогательной библиотеки, такой как Wait.for-ES6 (я автор), вы можете полностью избежать обратного вызова и закрытия, и теперь «блоки блокировки» работают, как ожидалось, и код прост.

Было бы очень полезно, если бы вы могли дать фрагмент кода, чтобы подчеркнуть разницу в общих сценариях (создание HTTP-запроса или вызов db).

Проверьте примеры Wait.for-ES6 , чтобы увидеть тот же код с обратными вызовами и с волокнами на основе генераторов.


Генераторы - одна из многих функций в предстоящем ES6. Поэтому в будущем их можно будет использовать в браузерах (прямо сейчас вы можете играть с ними в FF).

Генераторы являются конструкторами для s.next () s. Похоже на тарабарщину, поэтому в упрощенных выражениях они позволяют создавать объекты, которые позже будут доступны для повторения с помощью чего-то вроде циклов с использованием метода.for (var i = 0; i < 5; i++){ console.log( s.next().value ) }

Генераторы определяются аналогично функциям. Кроме того, что они есть и в них. * означает, что это генератор, next (with_params) похож на return.for (var n of seq()){ if (n >=5) break; console.log(n); }yield*

Например, это генератор:

throw()

Затем вы можете использовать этот генератор . Но в отличие от функции он не выполнит все и не даст вам результата, он просто создаст генератор. Выполняется только при запуске генератора. Здесь с s.next (); аналогичен возврату, но когда Итератор будет работать, он будет Наблюдателем генератора и продолжит работу над следующим выражением после следующего. Но когда назовется следующий , генератор будет функционировать * gen () { yield 5 ; выход 6 ; } пусть a = gen (); его исполнение. В этом случае он будет продолжать делать цикл while навсегда.var ID = 1; database.find({user : ID}, function(userInfo){ fileSystem.find(userInfo.name, function(key){ ftp.connect(ID, key, function(o){ console.log('Finally '+o); }) }) });function *logic(ID){ var userInfo = yield database.find({user : ID}); var key = yield fileSystem.find(userInfo.name); var o = yield ftp.connect(ID, key); console.log('Finally '+o); } var s = logic(1);PULL

Таким образом, вы можете повторить это с помощью

a.next()

или с конкретной конструкцией для генераторов:

pull

Эти основы о генераторах (вы можете посмотреть на pause, yield, a.next()и другие дополнительные конструкции). Обратите внимание, что речь идет о генераторах в ES6 (так что вы можете делать все это в узле и в браузере).

Но как эта бесконечная последовательность чисел имеет какое-либо отношение к обратному вызову?

Здесь важна функция * gen () { document . write ( 's observer:' , yield 1 ); } var a = gen (); var i = a . next (); while (! i . сделано ) { документ . написать ( '<br> итератора:' , я . значение ); i = a . следующий ( 100 ); } приостанавливает генератор. Итак, представьте себе, что у вас очень странная система, которая работает так:

У вас есть база данных с пользователями, и вам нужно найти имя пользователя с некоторым идентификатором, тогда вам нужно проверить в своей файловой системе ключ для имени этого пользователя, а затем вам нужно подключиться к некоторому ftp с идентификатором пользователя и ключом и сделать что-то после подключения. (Звучит смешно, но я хочу показать вложенные обратные вызовы).

Раньше вы писали бы что-то вроде этого:

yield 1

Это обратный вызов внутри обратного вызова внутри обратного вызова внутри обратного вызова. Теперь вы можете написать что-то вроде:

a.next

И затем используйте его. i.valueКак видите, нет вложенных обратных вызовов.

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


6

Генератор представляет собой комбинацию двух вещей: a Iteratorи an 1.

Итератор

Итератор - это то, что при вызове возвращает итерабельность, которую вы можете повторить. Начиная с ES6, все коллекции (Array, Map, Set, WeakMap, WeakSet) соответствуют контракту Iterable.

Генератор (итератор) является производителем. В итерации потребитель a.next(100)получает значение от производителя.

Пример:

spawn

Всякий раз, когда вы вызываете sync, вы по существу используете asyncценность от Iterator и pauseисполнения yield. При следующем вызове PULLвыполнение возобновляется из ранее приостановленного состояния.

наблюдатель

Генератор также является наблюдателем, с помощью которого вы можете отправить некоторые значения обратно в генератор. Объясняется лучше с примерами.

PUSH

Здесь вы можете видеть, что это используется как выражение, которое оценивает какое-то значение. Значение, которое оно оценивает, - это значение, отправленное как аргумент вызова функции.var i = a.next() // PULL dosomething(..., v => {...}) // PUSHa.next()

Таким образом, в первый раз v => {...}будет получено первое значение ( 1), а при продолжении итерации до следующего состояния мы отправим значение обратно генератору PUSH.

Где вы можете использовать это в Node.JS?

Генераторы широко используются с vфункцией (от taskJS или co), где функция принимает генератор и позволяет нам писать let delay = t => new Promise ( r => setTimeout ( r , t )); spawn ( function * () { // ожидание 100 мс и передача 1 let x = выходная задержка ( 100 ). then (() => 1 ); console . log ( x ); // 1 // ожидание 100 мс и отправить 2 пусть y = выходная задержка ( 100 ), затем (() => 2 ); console . log ( y ); // 2 }); хронический код синхронно. Это НЕ означает, что асинхронный код преобразуется в синхронный код / ??выполняется синхронно. Это означает, что мы можем написать код, который выглядит, но внутренне он по-прежнему . syncasync

Синхронизация блокируется; Async ОЖИДАЕТ. Написание кода, который блокирует, легко. При ожидании значения появляется в позиции назначения. При приостановке в позиции аргумента обратного вызова появляется значение

Когда вы используете итераторы, вы получаете PULLстоимость от производителя. Когда вы используете обратные вызовы, производитель resumeпередает значение в позицию аргумента обратного вызова.

yield promise

Здесь вы извлекаете значение из async...awaitи во втором, 0.11.2это обратный вызов, и значение PUSHизменяется в позицию аргумента vфункции обратного вызова.

Используя этот механизм pull-push, мы можем написать асинхронное программирование, как это,

$ node --harmony app.js 

Итак, глядя на вышеприведенный код, мы пишем асинхронный код, который выглядит так blocking(оператор yield ожидает 100 мс, а затем продолжит выполнение), но это на самом деле waiting. pauseИ resumeсвойство генератора позволяет нам сделать этот удивительный трюк.

Как это работает ?

Функция spawn использует для PULL состояние обещания от генератора, ждет, пока обетование будет разрешено, и ОТКЛЮЧАЕТ разрешенное значение обратно генератору, чтобы он мог его использовать.$ node --harmony_generators app.js

Используйте его сейчас

Итак, с генераторами и функцией spawn вы можете очистить весь ваш асинхронный код в NodeJS, чтобы выглядеть и чувствовать себя синхронным. Это облегчит отладку. Также код будет выглядеть аккуратно.

BTW, это подходит для JavaScript изначально для ES2017 - as . Но вы можете использовать их сегодня в ES2015 / ES6 и ES2016, используя функцию spawn, определенную в библиотеках - taskjs, co или bluebird$ iojs app.js


0

Чтобы использовать генераторы ES6 в узле, вам нужно либо установить node > = function*или iojs .

В узле вам нужно будет ссылаться на флаг гармонии:

()

или вы можете явно ссылаться на флаг генераторов

next()

Если вы установили iojs, вы можете опустить флаг гармонии.

next()

Для получения обзора на высоком уровне о том, как использовать генераторы, просмотрите этот пост .


0

Резюме:

yieldопределяет функцию генератора, которая возвращает объект-генератор. Особенность функции генератора заключается в том, что она не выполняется, когда она вызывается с использованием booleanоператора. Вместо этого возвращается объект итератора.

Этот итератор содержит trueметод. Метод итератора возвращает объект , который содержит значение свойства , которое содержит значение , выданное. Второе свойство возвращаемого объекта - это свойство next (), которое является (которое должно возвращаться, если выполняется функция генератора).function* IDgenerator() { var index = 0; yield index++; yield index++; yield index++; yield index++; } var gen = IDgenerator(); // generates an iterator object console.log(gen.next().value); // 0 console.log(gen.next().value); // 1 console.log(gen.next().value); // 2 console.log(gen.next()); // object, console.log(gen.next()); // object doneyieldyieldyield

Пример:

done

В этом примере мы сначала создаем объект итератора. На этом итераторном объекте мы можем вызвать async awaitметод, который позволяет нам перейти yieldк yieldзначению. Нам возвращен объект, который имеет как значение, так и doneсвойство.

Как это полезно?

  • Некоторые библиотеки и фреймворки могут использовать эту конструкцию для ожидания завершения асинхронного кода, например, redux-saga
  • asyncновый синтаксис, который позволяет вам ждать asyncсобытий, использует это под капотом. Знание работы генераторов даст вам лучшее представление о том, как работает эта конструкция.
JavaScript, Node.js, генератор, ECMAScript-6,
Похожие вопросы