¿Iterador y un generador en Javascript?

En los iteradores y generadores de la página de Mozilla hay una statement:

Si bien los iteradores personalizados son una herramienta útil, su creación requiere una progtwigción cuidadosa debido a la necesidad de mantener explícitamente su estado interno. Los generadores ofrecen una poderosa alternativa: le permiten definir un algoritmo iterativo escribiendo una única función que puede mantener su propio estado.

Con respecto a la explicación anterior, ¿no es posible escribir un algoritmo iterativo sin generadores, como:

Array[Symbol.iterator] = function(){ return { next: function(){ //logic return { value: "", done:false } } } } 

No puedo mover mi cabeza. ¿Alguien podría explicar cuál es la razón principal por la que crearon una alternativa? No me parece muy diferente.

Pueden parecer bastante similares en la superficie, pero se pueden usar de maneras muy diferentes.

Iteradores e iterables

Los iteradores se definen estrictamente: son objetos (los iteradores) que contienen una función next (y posiblemente algunas otras). Cada vez que se llama a la next función, se espera que devuelva un objeto con dos propiedades:

  • value : el valor actual del iterador
  • done : es el iterador terminado?

Por otro lado, un objeto iterable es un objeto que tiene una propiedad con una clave Symbol.iterator (que representa el símbolo más conocido @@iterator ). Esa tecla contiene una función que, cuando se llama, devuelve un nuevo iterador. Un ejemplo de un iterable:

 const list = { entries: { 0: 'a', 1: 'b' }, [Symbol.iterator]: function(){ let counter = 0; const entries = this.entries; return { next: function(){ return { value: entries[counter], done: !entries.hasOwnProperty(counter++) } } } } }; 

Su propósito principal, como su nombre lo sugiere, es proporcionar una interfaz que pueda ser iterada:

 for (let item of list) { console.log(item); } // 'a' // 'b' 

Generadores

Los generadores en cambio son mucho más versátiles. Es útil pensar en ellas como funciones que pueden pausarse y reanudarse.

Si bien pueden ser iterados (sus iterables proporcionan un método next ), pueden implementar procedimientos mucho más sofisticados y proporcionar una comunicación de entrada / salida a través de su next método.

Un generador simple:

 function *mygen () { var myVal = yield 12; return myVal * 2; } const myIt = mygen(); const firstGenValue = myIt.next().value; // Generator is paused and yields the first value const result = myIt.next(firstGenValue * 2).value; console.log(result); // 48 

Delegación del generador

Los generadores pueden delegar a otro generador:

 function *mydelgen(val) { yield val * 2; } function *mygen () { var myVal = yield 12; yield* mydelgen(myVal); // delegate to another generator } const myIt = mygen(); const val = myIt.next().value; console.log(val); console.log(myIt.next(val).value); console.log(myIt.next().value); 

Generadores y Promesas

Generadores y promesas juntos pueden crear una especie de iterador asíncrono automático con la ayuda de utilidades como co .

 co(function *(){ // resolve multiple promises in parallel var a = Promise.resolve(1); var b = Promise.resolve(2); var c = Promise.resolve(3); var res = yield [a, b, c]; console.log(res); // => [1, 2, 3] }).catch(onerror); 

En conclusión

Entonces, en conclusión, se podría decir que el propósito principal de los iteradores es crear una interfaz para que los objetos personalizados sean iterados, mientras que los generadores proporcionan una gran cantidad de posibilidades para flujos de trabajo síncronos y asíncronos:

  • funciones con estado
  • delegación generadora
  • generadores y promesas
  • CSP

etc.

¿No es posible escribir un algoritmo iterativo sin generadores?

No, no es. Sí, es posible escribir cada algoritmo generador como un iterador personalizado, pero la // logic en su código será mucho más complicada. El énfasis de la statement es que ya no será iterativo, será recursivo.

Como ejercicio, aquí hay una función de generador iterativo bastante simple:

 function* traverseTree(node) { if (node == null) return; yield* traverseTree(node.left); yield node.value; yield* traverseTree(node.right); } 

Intente reescribirlo como un iterador personalizado. Ya sea que te quedes estancado o lo hagas, te mostrará cuál es la diferencia.