Что такое генераторы ES6 и как их использовать в node.js?
Сегодня я был на встрече node.js, и кто-то, кого я там встречал, сказал, что node.js имеет генераторы es6. Он сказал, что это огромное улучшение по сравнению с программированием стиля обратного вызова, и изменит ландшафт node. Айц, он сказал что-то о стеке вызовов и исключениях.
Я просмотрел их, но на самом деле не нашел никакого ресурса, который объясняет их в удобном для новичков. Каков общий обзор генераторов высокого уровня и как отличаются (или лучше?), Чем обратные вызовы?
PS: Было бы очень полезно, если бы вы могли дать фрагмент кода, чтобы подчеркнуть разницу в общих сценариях (создание HTTP-запроса или вызов db).
Ответы
Ответ 1
Генераторы, волокна и сопрограммы
"Генераторы" (помимо "генераторов" ) также являются основными блоками зданий "fiber" или "coroutines" . С помощью волокон вы можете "приостановить" функцию, ожидающую возвращения асинхронного вызова, эффективно избегая объявления функции обратного вызова "на месте" и создания "закрытия". Попрощайтесь с адским обратным вызовом.
Закрытие и попытка
... он сказал что-то о стеке вызовов и исключениях
Проблема с "закрытием" заключается в том, что даже если они "магически" сохраняют состояние локальных переменных для обратного вызова, "закрытие" не может содержать стек вызовов.
В момент обратного вызова, как правило, вызывающая функция вернулась давным-давно, поэтому любой блок "catch" вызывающей функции не может перехватывать исключения в самой функции async или обратном вызове. Это представляет большую проблему. Из-за этого вы не можете комбинировать обратные вызовы + замыкания с исключением catching.
Wait.for
... и изменит ландшафт node
Если вы используете генераторы для создания вспомогательной библиотеки типа Wait.for-ES6 (я автор), вы можете полностью избежать обратного вызова и закрытия, и теперь "блоки блокировки" работают, как ожидалось, и код прост.
Было бы очень полезно, если бы вы могли дать фрагмент кода, чтобы подчеркнуть разницу в общих сценариях (создание HTTP-запроса или вызов db).
Проверьте Wait.for-ES6 примеры, чтобы увидеть тот же код с обратными вызовами и с волокнами на основе генераторов.
Ответ 2
Генераторы являются одним из многих функций в предстоящем ES6. Таким образом, в будущем их можно будет использовать в браузерах (прямо сейчас вы можете играть с ними в FF).
Генераторы являются конструкторами для итераторов. Похоже на тарабарщину, поэтому в упрощенных выражениях они позволяют создавать объекты, которые позже можно будет перебирать с помощью чего-то вроде циклов с использованием метода .next()
.
Генераторы определяются аналогично функциям. Кроме того, в них есть *
и yield
. * означает, что это генератор, выход аналогичен возврату.
Например, это генератор:
function *seq(){
var n = 0;
while (true) yield n++;
}
Затем вы можете использовать этот генератор с var s = seq()
. Но в отличие от функции он не выполнит все и не даст вам результата, он просто создаст генератор. Только когда вы запустите s.next()
, генератор будет выполнен. Здесь доходность аналогична возврату, но когда выход будет работать, он остановит генератор и продолжит работу над следующим выражением после следующего. Но когда будет вызываться следующий s.next()
, генератор возобновит выполнение. В этом случае он будет продолжать делать цикл while навсегда.
Итак, вы можете повторить это с помощью
for (var i = 0; i < 5; i++){
console.log( s.next().value )
}
или с конкретной конструкцией для генераторов:
for (var n of seq()){
if (n >=5) break;
console.log(n);
}
Это основы генераторов (вы можете посмотреть yield*
, next(with_params)
, throw()
и другие дополнительные конструкции). Обратите внимание, что речь идет о генераторах в ES6 (так что вы можете сделать все это в node и в браузере).
Но как эта бесконечная последовательность чисел имеет какое-либо отношение к обратному вызову?
Важная вещь здесь заключается в том, что выход приостанавливает генератор. Итак, представьте, что у вас очень странная система, которая работает таким образом:
У вас есть база данных с пользователями, и вам нужно найти имя пользователя с некоторым идентификатором, тогда вам нужно проверить в своей файловой системе ключ для этого имени пользователя, а затем вам нужно подключиться к некоторым ftp с идентификатором пользователя и ключ и сделать что-то после подключения. (Звучит смешно, но я хочу показать вложенные обратные вызовы).
Раньше вы писали бы что-то вроде этого:
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);
И затем используйте его with s.next();
Как вы видите, нет вложенных обратных вызовов.
Поскольку node сильно использует вложенные обратные вызовы, вот почему парень говорил, что генераторы могут изменять ландшафт node.
Ответ 3
Генератор представляет собой комбинацию двух вещей - a Iterator
и a Observer
.
Итератор
Итератор - это то, что при вызове возвращает итерабельность, которую вы можете повторить. Начиная с ES6, все коллекции (Array, Map, Set, WeakMap, WeakSet) соответствуют контракту Iterable.
Генератор (итератор) является производителем. В итерации потребитель PULL
получает значение от производителя.
Пример:
function *gen() { yield 5; yield 6; }
let a = gen();
Всякий раз, когда вы вызываете a.next()
, вы по существу PULL
-интегрируете значение из Iterator и pause
исполнение в yield
. При следующем вызове a.next()
выполнение возобновляется из ранее приостановленного состояния.
Observer
Генератор также является наблюдателем, с помощью которого вы можете отправить некоторые значения обратно в генератор. Объясняется лучше с примерами.
function *gen() {
document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
document.write('<br>iterator:', i.value);
i = a.next(100);
}
Ответ 4
Чтобы использовать генераторы ES6 в node, вам нужно либо установить node >= 0.11.2
, либо iojs.
В node вам нужно будет указать флаг гармонии:
$ node --harmony app.js
или вы можете явно ссылаться на флаг генераторов
$ node --harmony_generators app.js
Если вы установили iojs, вы можете опустить флаг гармонии.
$ iojs app.js
Для получения обзора высокого уровня о том, как использовать генераторы, проверить этот пост.