Возможно ли, чтобы reset генератор ECMAScript 6 в исходное состояние?
Учитывая предоставленный (очень простой) генератор, возможно ли вернуть генератор обратно в исходное состояние для повторного использования?
var generator = function*() {
yield 1;
yield 2;
yield 3;
};
var iterable = generator();
for (let x of iterable) {
console.log(x);
}
// At this point, iterable is consumed.
// Is there a method for moving iterable back
// to the start point by only without re-calling generator(),
// (or possibly by re-calling generator(), only by using prototype
// or constructor methods available within the iterable object)
// so the following code would work again?
for (let x of iterable) {
console.log(x);
}
Я хотел бы иметь возможность передавать итерируемую часть в какую-то другую область, перебирать ее, делать некоторые другие вещи, а затем снова иметь возможность перебирать ее в той же области.
Ответы
Ответ 1
Если ваше намерение
в какой-то другой области, перебирайте его, делайте некоторые другие вещи, затем сможете повторять его снова позже в той же области видимости.
Тогда единственное, что вам не следует делать, это передать итератор, вместо этого передать генератор:
var generator = function*() {
yield 1;
yield 2;
yield 3;
};
var user = function(generator){
for (let x of generator()) {
console.log(x);
}
for (let x of generator()) {
console.log(x);
}
}
Или просто создайте итератор "round robin" и проверьте при повторении
var generator = function*() {
while(true){
yield 1;
yield 2;
yield 3;
}
};
for( x in i ){
console.log(x);
if(x === 3){
break;
}
}
Ответ 2
В этот момент истребитель потребляется.
Это означает, что его внутренний [[GeneratorState]] completed
.
Есть ли способ перемещения итерации обратно в начальную точку только без генератора повторного вызова()
Нет. Spec указывает
Как только генератор входит в "завершенное" состояние, он никогда не покидает его, и связанный с ним контекст выполнения никогда не возобновляется. Любое состояние выполнения, связанное с генератором, может быть отброшено в этот момент.
или, возможно, с помощью генератора вызовов(), только с использованием прототипа или методы конструктора, доступные в итерируемом объекте
Нет. Хотя это явно не указано в спецификации, в итерируемом объекте доступно больше свойств экземпляра, чем [[GeneratorState]] и [[GeneratorContext]].
Однако информативные "отношения объектов генератора" grapic гласят:
Каждая функция генератора имеет связанный прототип, который не имеет свойства конструктора. Следовательно, экземпляр генератора не предоставляет доступ к его генераторной функции.
Я хотел бы иметь возможность передавать итерацию в другую область
Передайте функцию генератора. Или что-то, что дает новые экземпляры генератора.
Ответ 3
Насколько я могу сказать, это невозможно. Per эта полезная вики и черновик версии ES6 для генераторов, как только вы вернетесь из него (а не уступите), он помещает его в состояние "closed"
, и нет способа вернуть его в состояние "newborn"
, из которого начинается новый генератор.
Вам может потребоваться передать обратный вызов в другую область для создания нового генератора. В качестве рабочего решения вы даже можете добавить этот обратный вызов как настраиваемый метод для генератора, который вы отправили в другую область, если хотите, и этот обратный вызов создаст новый генератор для другой области.
Если вы думаете о том, как работают генераторы, им придется выполнить с нуля до reset их начальное состояние, и просто нет оснований для его поддержки. Это было бы аналогичным спросить, почему вы не можете просто повторно выполнить конструктор на существующем объекте и ожидать, что в одном и том же объекте будет виргинский объект. В то время как все это технически выполнимо, оно волосатое, чтобы сделать работу правильной, и нет никаких оснований для ее поддержки. Если вы хотите девственный объект, просто создайте новый. То же самое с генератором.
Это немного взломать, но это любопытная штука. Вы могли бы сделать генератор, который повторялся. Предположим, что ваш генератор работал следующим образом:
var generator = function*() {
while (true) {
yield 1;
yield 2;
yield 3;
yield null;
}
};
var iterable = generator();
for (let x of iterable) {
if (x === null) break;
console.log(x);
}
// generator is now in a state ready to repeat again
Я могу легко увидеть, как это может быть анти-шаблон, хотя, если вы когда-нибудь это сделаете:
for (let x of iterable) {
console.log(x);
}
У вас будет бесконечный цикл, поэтому его нужно будет использовать с большой осторожностью. FYI, приведенная выше вики показывает примеры бесконечной последовательности Фибоначчи, поэтому бесконечный генератор, безусловно, предполагается.
Ответ 4
В соответствии с черновик версии ES6,
Как только генератор входит в состояние "completed"
, он никогда не покидает его, и связанный с ним контекст выполнения никогда не возобновляется. Любое состояние выполнения, связанное с генератором, может быть отброшено в этот момент.
Таким образом, он не может reset после завершения. Это также имеет смысл. Мы называем это генератором по какой-то причине:)
Ответ 5
Вы также можете создать свой генератор reset следующим образом:
let iterable = generator();
function* generator(){
yield 1;
yield 2;
yield 3;
iterable = generator();
}
for (let x of iterable) {
console.log(x);
}
//Now the generator has reset the iterable and the iterable is ready to go again.
for (let x of iterable) {
console.log(x);
}
Я лично не знаю плюсов и минусов этого. Просто, что он работает так, как вы ожидаете, переназначив итерацию каждый раз, когда генератор закончит.
РЕДАКТИРОВАТЬ: С более подробными сведениями о том, как эта работа я бы рекомендовал просто использовать генератор, например Azder Показанный:
const generator = function*(){
yield 1;
yield 2;
yield 3;
}
for (let x of generator()) {
console.log(x);
}
for (let x of generator()) {
console.log(x);
}
Рекомендуемая вами версия не позволит вам запускать итерации, если она когда-либо провалится... Например, если вы ждали на одном URL-адресе позвонить другому. Если первый URL-адрес не удался, вам придется обновить приложение до того, как он сможет снова попробовать этот первый выход.
Ответ 6
Всякий раз, когда вам нужно "reset" повторить, просто отбросьте старый и сделайте новый.
var generator = function*() {
yield 1;
yield 2;
yield 3;
};
const makeIterable = () => generator()
for (let x of makeIterable()) {
console.log(x);
}
// At this point, iterable is consumed.
// Is there a method for moving iterable back
// to the start point by only without re-calling generator(),
// (or possibly by re-calling generator(), only by using prototype
// or constructor methods available within the iterable object)
// so the following code would work again?
for (let x of makeIterable()) {
console.log(x);
}
Ответ 7
Я думаю, что это касается не генератора, а итератора, который на самом деле "делает работу". Чтобы сбросить итерацию, вам просто нужно создать новый итератор. Я бы, вероятно, использовал бы тупую функцию более высокого порядка, как это:
function *foo() {
yield 1;
yield 2;
yield 3;
}
const iterateFromStart = (func) => {
// every invocation creates a brand new iterator
const iterator = func();
for (let val of iterator) {
console.log(val)
}
}
iterateFromStart(foo); // 1 2 3
iterateFromStart(foo); // 1 2 3