ES6: Los entresijos de Generator
Visión general
-
La función de generador es una solución de programación asíncrona proporcionada por ES6, que puede entenderse como una máquina de estados que encapsula múltiples estados internos. -
-
La ejecución de la función Generator devolverá un objeto traverser, que puede atravesar cada estado dentro de la función por turno;
-
La función Generador tiene dos características:
- Hay un asterisco entre la función y el nombre de la función;
- La declaración de rendimiento se utiliza dentro del cuerpo de la función para definir diferentes estados internos.
-
Ejemplo:
function*helloWorldGenerator(){ yield '听不见!'; yield '重来!'; return '很有精神!' } let hw = helloWorldGenerator(); hw.next(); // { value : '听不见!',done : false } hw.next(); // { value : '重来!',done : false } hw.next(); // { value : '很有精神!',done : true } hw.next(); // { value : undefined , done : true }
-
Con esta función, puede realizar el mecanismo de programación asincrónica de la función;
expresión de rendimiento
-
El mecanismo de ejecución del siguiente método del traverser es el siguiente:
- Cuando se encuentre una declaración de rendimiento, la ejecución de las siguientes operaciones se suspenderá y el valor de la expresión de rendimiento se devolverá como el valor del atributo de valor del objeto devuelto;
- La próxima vez que se llame a la siguiente llamada para continuar la ejecución, hasta que se encuentre la siguiente declaración de rendimiento;
- Si no hay rendimiento, ejecute directamente a la declaración de retorno;
- Si no hay una declaración de retorno, se devuelve un objeto cuyo valor no está definido.
-
El rendimiento debe colocarse en la función Generador antes de que pueda usarse;
-
Si se usa rendimiento en otra expresión, ¡se deben agregar paréntesis!
console.log('hello' + yield 123); ///error console.log('hello' + (yield 123)); //ok
Parámetros del siguiente método
-
El siguiente método puede tomar un parámetro, que se utilizará como valor de retorno de la declaración anterior. Cuando next no tiene parámetros, devuelve undefined;
function* f(){ for(let i=0;ture;i++){ let reset = yield i; if(reset){ i = -1; } } } let g = f(); g.next() //{value : 0,done : false}; g.next() //{value : 1,done : false}; g.next(true) //{value : 0,done : false};
para ... de método
-
El bucle for ... of puede atravesar automáticamente el objeto Iterator generado por la función Generator, eliminando la necesidad de utilizar el siguiente método;
function* foo(){ yield 1; yield 2; yield 3; yield 4; yield 5; } for (let v of foo()){ console.log(v); }
rendimiento * expresión
-
Si llama a otra función de generador en un generador, el valor predeterminado es sin efecto;
function* foo(){ yield 'a'; yield 'b'; } function bar(){ yield 1; foo(); yield 2; } for(let i of bar()){ console.log(i); } >>1 >>2
-
Puede verse que la llamada directa no puede ejecutar foo () en for ... of;
-
En este momento, debe usar la declaración yield *, por lo que el código anterior se puede modificar de la siguiente manera:
function* foo(){ yield 'a'; yield 'b'; } function bar(){ yield 1; yield* foo(); yield 2; } for(let i of bar()){ console.log(i); } >>1 >>'a' >>'b' >>2
-
Si rendimiento * va seguido de una matriz, los miembros de la matriz se atravesarán:
function* foo(){ yield [1,2,3,4,5]; } foo().next(); //[1,2,3,4,5] function* foo(){ yield* [1,2,3,4,5]; } foo().next(); //{value : 1,done : false}
-
La expresión yield * puede atravesar rápidamente todos los miembros de la matriz anidada;
function* iterTree(tree){ if(Array.isArray(tree)){ for(let i=0;i<tree.length;i++); yield* iterTree(tree[i]); }else{ yield tree; } } const tree = [1,[2,3],[4,5]]; for(let x of iterTree(tree)){ console.log(x); } >> 1 >> 2 >> 3 >> 4 >> 5
Esto de la función Generador
-
El iterador devuelto por la función Generator es una instancia de la función Generator original, que también hereda el prototipo de la función original;
-
Por lo tanto, el iterador de la función Generator no puede acceder directamente a las propiedades de la función original;
function* foo(){ this.a = 1; } let obj = foo(); console.log(obj.a); //undefined
-
Para obtener las propiedades de la función original, puede obtenerlas a través de un método flexible: use el método de llamada para vincularlo dentro de la función Generator;
function* foo(){ this.a = 1; yield this.b = 2; yield this.c = 3; } let obj = { }; let bar = foo.call(obj); bar.next(); //{value : 2 , done : false}; bar.next(); //{value : 3 , done : false}; console.log(obj.a) // 1 console.log(obj.b) // 2 console.log(obj.c) // 3