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

17

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

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

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

задан tldr 17.09.2013 в 07:45
источник

4 ответа

13

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

«Генераторы» (помимо «генераторов») также являются основными блоками зданий «волокна» или «сопрограммы» «. С помощью волокон вы можете «приостановить» функцию, ожидающую возвращения асинхронного вызова, эффективно избегая объявления функции обратного вызова «на месте» и создания «закрытия». Попрощайтесь с адским обратным вызовом.

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

  

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

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

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

Wait.for

  

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

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

  

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

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

ответ дан Lucio M. Tato 15.02.2014 в 21:36
8

Генераторы являются одним из многих функций в предстоящем 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 (поэтому вы можете делать все это в узле и в браузере).

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

Важное замечание здесь заключается в том, что выход приостанавливает генератор. Итак, представьте, что у вас очень странная система, которая работает таким образом:

У вас есть база данных с пользователями, и вам нужно найти имя пользователя с некоторым идентификатором, тогда вам нужно проверить в своей файловой системе ключ для имени этого пользователя, а затем вам нужно подключиться к некоторому 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(); Как вы видите, нет вложенных обратных вызовов.

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

    
ответ дан Salvador Dali 26.07.2014 в 04:50
6

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

Итератор

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

  

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

Пример:

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);
}

Здесь вы можете увидеть, что yield 1 используется как выражение, которое оценивает какое-то значение. Значение, которое оно оценивает, представляет собой значение, отправленное в качестве аргумента для вызова функции a.next .

Итак, впервые i.value будет первым значением, полученным ( 1 ), и при продолжении итерации в следующее состояние мы отправим значение обратно генератору, используя a.next(100) .

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

Генераторы широко используются с функцией spawn (от taskJS или co), где функция принимает генератор и позволяет нам писать асинхронный код синхронно. Это НЕ означает, что асинхронный код преобразуется в синхронный код / ​​выполняется синхронно. Это означает, что мы можем написать код, который выглядит как sync , но внутри он все еще async .

  

Синхронизация - БЛОКИРОВКА; Async ОЖИДАЕТ. Написание кода, который блокирует, легко. Когда PULLing, значение появляется в позиции назначения. Когда PUSHing, значение появляется в позиции аргумента обратного вызова

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

var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH

Здесь вы извлекаете значение из a.next() , а во втором v => {...} - это обратный вызов, а значение PUSH ed в позиции аргумента v функции обратного вызова.

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

let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
  // wait for 100 ms and send 1
  let x = yield delay(100).then(() => 1);
  console.log(x); // 1

   // wait for 100 ms and send 2
  let y = yield delay(100).then(() => 2);
  console.log(y); // 2
});

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

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

Функция spawn использует yield promise для PULL состояния обещания от генератора, ждет, пока обещание будет разрешено, и ОТПУСКАЕТ разрешенное значение обратно генератору, чтобы он мог его использовать.

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

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

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

    
ответ дан Boopathi Rajaa 17.08.2015 в 00:03
0

Чтобы использовать генераторы ES6 в узле, вам нужно либо установить узел & gt; = 0.11.2 , либо iojs .

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

$ node --harmony app.js 

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

$ node --harmony_generators app.js

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

$ iojs app.js

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

ответ дан Denim Demon 29.01.2015 в 11:48