name method functions ecmascript javascript generator ecmascript-harmony ecmascript-6

javascript - method - ¿Es posible restablecer un generador ECMAScript 6 a su estado inicial?



method name javascript (7)

Mi pregunta es la siguiente: dado el generador proporcionado (muy simple), ¿es posible devolver el generador a su estado original para usarlo nuevamente?

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

Me gustaría poder pasar el iterable a otro ámbito, iterar sobre él, hacer otras cosas, y luego poder iterar sobre él más adelante en ese mismo alcance.


En este punto, iterable se consume.

Lo que significa que su [[GeneratorState] interno] se ha completed .

¿Existe un método para volver iterable al punto de inicio solo sin volver a llamar al generador ()

No. Los estados espec.

Una vez que un generador ingresa al estado "completado", nunca lo abandona y su contexto de ejecución asociado nunca se reanuda. Cualquier estado de ejecución asociado con el generador se puede descartar en este punto.

o posiblemente volviendo a llamar al generador (), solo usando métodos prototipo o constructor disponibles dentro del objeto iterable

No. Aunque no se indica explícitamente en la especificación, no hay más propiedades específicas de la instancia disponibles en el objeto iterable que [[GeneratorState]] y [[GeneratorContext]].

Sin embargo, el gráfico informativo "Relaciones de objetos generadores" dice:

Cada función del generador tiene un prototipo asociado que no tiene una propiedad de constructor. Por lo tanto, una instancia de generador no expone el acceso a su función de generador.

Me gustaría poder pasar el iterable a otro ámbito.

Pase la función del generador en su lugar. O algo que genere nuevas instancias de generador.


Creo que esto no es una preocupación de un generador, sino de un iterador, que en realidad "hace el trabajo". Para restablecer una iteración, solo necesita crear un nuevo iterador. Probablemente usaría una función de orden superior tonta como esta:

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


Lo mejor que puedo decir es que no es posible. Según esta wiki útil y la versión de borrador de ES6 en los generadores, una vez que haya regresado de ella (en lugar de ceder), la coloca en el estado "closed" y no hay manera de volver a la situación "newborn" que es como un nuevo generador comienza.

Es posible que tenga que pasar una devolución de llamada a su otro ámbito para crear un nuevo generador. Como solución alternativa, incluso podría agregar esa devolución de llamada como un método personalizado en el generador que envió al otro alcance si así lo desea y esa devolución de llamada crearía un nuevo generador para el otro alcance.

Si piensa en cómo funcionan los generadores, tendrían que ejecutarse desde cero para restablecer su estado inicial y simplemente no hay razón para admitirlo. Sería análogo preguntarle por qué no puede simplemente volver a ejecutar el constructor en un objeto existente y esperar tener un objeto virgen en el mismo objeto. Si bien todo es técnicamente factible, es difícil trabajar bien y realmente no hay razón para apoyarlo. Si quieres un objeto virgen, simplemente crea uno nuevo. Lo mismo con un generador.

Esto es un poco de un hack, pero algo curioso para contemplar. Podrías hacer un generador que se repitiera. Supongamos que su generador funcionó así:

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

Sin embargo, puedo ver fácilmente cómo esto podría ser un anti-patrón porque si alguna vez haces esto:

for (let x of iterable) { console.log(x); }

Tendrás un bucle infinito, por lo que debería usarse con mucho cuidado. Para su información, el wiki anterior muestra ejemplos de una secuencia infinita de Fibonacci por lo que ciertamente se contempla un generador infinito.


Según la versión borrador de ES6 ,

Una vez que un generador ingresa al estado "completed" , nunca lo abandona y su contexto de ejecución asociado nunca se reanuda. Cualquier estado de ejecución asociado con el generador se puede descartar en este punto.

Por lo tanto, no hay manera de restablecerlo una vez que se haya completado. También tiene sentido ser así. Lo llamamos un generador, por una razón :)


Si tu intencion es

para algún otro ámbito, iterar sobre él, hacer otras cosas, y luego ser capaz de iterar sobre él más tarde en ese mismo ámbito.

Entonces, lo único que no deberías intentar es pasar el iterador, en vez de eso, pasa el generador:

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

O simplemente haga un iterador "round robin" y verifique mientras itera

var generator = function*() { while(true){ yield 1; yield 2; yield 3; } }; for( x in i ){ console.log(x); if(x === 3){ break; } }


Siempre que necesite "reiniciar" un iterable, simplemente deseche el anterior y cree uno nuevo.

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


También puede hacer que su generador reinicie su iterable de esta manera:

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

No conozco personalmente los pros y los contras de hacer esto. Solo que funciona como se esperaría al reasignar el iterable cada vez que finaliza el generador.

EDITAR: Con más conocimiento de cómo funciona este trabajo, recomendaría usar el generador como Showed:

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

La versión que recomendé evitará que puedas ejecutar las iteraciones si alguna vez falla ... Por ejemplo, si esperabas en una URL para llamar a otra Si la primera url falla, deberías actualizar tu aplicación antes de que pueda probar ese primer rendimiento nuevamente.