Подтвердить что ты не робот

Генераторы Javascript: понимание

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

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

(function *() {
     // Wait until users have be got and put into value of `results`
     var results = yield db.get("users");
     // And continue
     view.display(results);
})();

Вместо:

db.get("user", function(results) {
    view.display(results);
});

Правильно, все хорошо и хорошо, пока я не попытаюсь написать свои собственные генераторы. Я столкнулся с несколькими нажатиями:

  • Первый пример кода, который я выше, не будет запущен, потому что нет ничего, чтобы перебирать генератор, правильно? Некоторые более высокие существа должны называть .next где-то, верно?
  • Весь API должен быть переписан вплоть до вызовов ввода-вывода для поддержки генераторов, исправить?
  • Из того, что я нахожу, yield, похоже, ждет ожидания большинства наиболее распространенных случаев использования, тогда как в части реализации (read: return value to/inside db.get) yield кажется, стоит для отправки этого значения назад к текущему ожидающему блоку, чтобы возобновить выполнение.

Возьмем, например:

function *fn() {
    yield 1;
    yield "a";
}

var gen = fn();
gen.next(); // 1
gen.next(); // "a";

yield в этом контексте отправляет значения обратно вниз, а не ждет результатов. В первом примере выше он ожидает результатов от db.get и возобновляет выполнение вместо "возврата" или отправки значения. Если случай db.get имеет значение true, не является ли это синхронным? Я имею в виду, это не то же самое, что:

(function() {
     //Wait for the results
    var results = fs.readFileSync("users.txt");
    // Use results
    view.display(results);
})();

К сожалению, если это ясно из этого вопроса (возможно, единственное, что ясно), я не понимаю генераторов. Надеюсь, я мог бы получить некоторое представление здесь.

4b9b3361

Ответ 1

TL; DR: сущность генератора контролирует приостановку выполнения кода.

Для самого генератора вы можете обратиться к this.

Подводя итог, нужно выделить три компонента: 1. функция генератора 2. генератор 3. сгенерированный результат

Функция генератора - это просто function со звездой в голове и (необязательно) yield в своем теле.

function *generator() {
  console.log('Start!');
  var i = 0;
  while (true) {
    if (i < 3)
      yield i++;
  }
}

var gen = generator();
// nothing happens here!!

Функция генератора сама по себе не делает ничего, кроме возврата генератора, в приведенном выше случае, gen. Здесь нет выхода консоли, потому что только после того, как вызванный метод генератора next будет вызван, будет выполняться тело функции генератора. Генератор имеет несколько методов, наиболее важным из которых является next. next запускает код и возвращает результат генератора.

var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}

ret выше - результат генератора. Он имеет два свойства: value, значение, которое вы получаете в функции генератора, и done - флаг, указывающий, возвращается ли функция генератора.

console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}

В этот момент никто не ожидает, что вы поймете генератор, по крайней мере, не асинхронную мощность генератора.

Проще говоря, генератор имеет две функции:

  • можно выпрыгнуть из функции и дать внешнему коду определить, когда нужно вернуться в функцию.
  • контроль асинхронного вызова может выполняться вне вашего кода

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

Посмотрите образец снова:

var gen = generator();
console.log('generated generator');
console.log(gen.next().value);
// mock long long processing
setTimeout(function() {
  console.log(gen.next().value);
  console.log('Execute after timer fire');
}, 1000);
console.log('Execute after timer set');

/* result:
    generated generator
    start
    0
    Execute after timer set
    1
    Execute after timer fire
*/

См? Сама функция генератора не обрабатывает обратный вызов. Внешний код делает.

База здесь. Вы можете разработать этот код для поддержки полной асинхронности, сохраняя при этом функцию генератора, такую ​​как синхронизация.

Например, предположим, что geturl является асинхронным вызовом, который возвращает объект promise. вы можете написать var html = yield getUrl('www.stackoverflow.com'); Это выйдет за пределы вашего кода. И внешний код будет делать такие вещи, как:

var ret = gen.next();
ret.then(function (fetchedHTML) {
  // jumps back to your generator function
  // and assign fetchHTML to html in your code
  gen.next(fetchedHTML);
});

Для получения более полного руководства обратитесь к this. И репозиторий, например co, galaxy, suspend и т.д.

Ответ 2

ни один из асинхронных файлов, если они являются частью генераторов. генераторы просто приостанавливают и возобновляют блоки кода. вся асинхронная магия происходит, когда вы используете то, что я называю "генераторным движком", например https://github.com/visionmedia/co.

в основном, то, что gen.next() делает, возвращает последнее значение yield ed и позволяет вам вернуть значение, если yield присваивается что-то, например. var something = yield 1. поэтому, если у вас есть блок кода:

function* genfun() {
  var a = yield 1
  var b = yield 2
}

var gen = genfun()

gen.next() // returns the first yielded value via `{value: 1}`
gen.next(1) // sets `a` as 1, returns the next yielded value via `{value: 2}`
gen.next(2) // sets `b` as 2, the generator is done, so it returns `{done: true}`

gen.throw(err) - это то же самое, что и в следующем, за исключением того, что вместо переменной присваивается ошибка.

так работают двигатели потока управления - вы получаете следующее значение, которое, вероятно, является обратным вызовом или чем-то еще. выполнить обратный вызов и не gen.next() до завершения обратного вызова.

Ответ 3

Два примера не совпадают. Когда вы уходите, функция теперь эффективно становится обратным вызовом, ожидающим исполнения, когда db.get( "users" ) заканчивается. Таким образом, функция не блокирует выполнение других функций. Подумайте об этом как о способе превратить синхронные функции в асинхронные функции, дав системе знать, что вы можете приостановить работу в определенных точках.