node - yield javascript
Generadores de Javascript: entendiéndolos (3)
Estoy bastante seguro de que mi comprensión de los generadores está intrínsecamente dañada. Todos los recursos en línea parecen estar en conflicto y se convierten en una experiencia de aprendizaje increíblemente difícil y confusa.
Por lo que entiendo, la palabra clave de yield
permite que un bloque de código actualmente en ejecución espere un valor en lugar de lanzar el código restante para que se ejecute dentro de una devolución de llamada. Entonces, como la mayoría de los tutoriales han señalado, puedes usar esto:
(function *() {
// Wait until users have be got and put into value of `results`
var results = yield db.get("users");
// And continue
view.display(results);
})();
En lugar de:
db.get("user", function(results) {
view.display(results);
});
Bien, todo está bien hasta que intento escribir mis propios generadores. Me he topado con varios enganches:
- El primer código de ejemplo I anterior no se ejecutará porque no hay nada que iterar sobre el generador, ¿correcto? Algún ser superior tiene que llamar al.
.next
algún lugar, ¿verdad? - La API completa deberá reescribirse hasta las llamadas de E / S para que sean compatibles con los generadores, ¿correcto?
- De lo que reconozco, el
yield
parece esperar el valor de la mayoría de los casos de uso general, mientras que en la parte de la implementación (leer: devolver el valor a / dentro dedb.get
) elyield
parece devolver el valor al bloque actualmente en espera para reanudarse ejecución
Tomar como ejemplo:
function *fn() {
yield 1;
yield "a";
}
var gen = fn();
gen.next(); // 1
gen.next(); // "a";
yield
en ese contexto es enviar valores de nuevo hacia abajo en lugar de esperar los resultados. En el primer ejemplo anterior, espera los resultados de db.get
y reanuda la ejecución en lugar de "devolver" o devolver un valor. Si el caso db.get
es verdadero, ¿no es esto inherentemente síncrono? Quiero decir, no es exactamente lo mismo que:
(function() {
//Wait for the results
var results = fs.readFileSync("users.txt");
// Use results
view.display(results);
})();
Desafortunadamente, si queda claro de esta pregunta (probablemente lo único claro) es que no entiendo a los generadores. Con suerte, podría tener una idea aquí.
Los dos ejemplos no son los mismos. Cuando cede, la función ahora se convierte efectivamente en una devolución de llamada, a la espera de que se ejecute cuando finalice db.get ("usuarios"). De esta manera, la función no bloquea la ejecución de otra función. Piense en ello como una manera de convertir las funciones síncronas en funciones asíncronas al hacerle saber al sistema que puede hacer una pausa en ciertos puntos.
Nada de lo asíncrono si forma parte de los generadores. Los generadores simplemente hacen una pausa y reanudan los bloques de código. toda la magia asíncrona ocurre cuando usas lo que yo llamo un "motor generador" como co .
básicamente, lo que hace gen.next()
es devolver el último valor ed de yield
y le permite devolver un valor si el yield
se asigna a algo, ej. var something = yield 1
. así que si tienes el bloque de código:
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)
es el mismo que el siguiente, excepto que el error se produce en lugar de asignarse a una variable.
Así es como funcionan los motores de flujo de control: obtienes el siguiente valor que probablemente sea una devolución de llamada o algo así. ejecute la devolución de llamada y no gen.next()
hasta que gen.next()
la devolución de llamada.
TL; DR: la esencia del generador es controlar la suspensión de la ejecución del código.
Para generador en sí, puede referirse a esto .
En resumen, hay tres componentes que debe distinguir: 1. función del generador 2. generador 3. resultado generado
La función del generador es simplemente la function
con estrella en la cabeza y (opcional) yield
en su cuerpo.
function *generator() {
console.log(''Start!'');
var i = 0;
while (true) {
if (i < 3)
yield i++;
}
}
var gen = generator();
// nothing happens here!!
La función del generador en sí no hace nada más que devolver un generador, en el caso anterior, gen
. No hay salida de consola aquí porque solo después de que se llame al next
método del generador devuelto , se ejecutará la función de cuerpo del generador . El generador tiene varios métodos, de los cuales el más importante es el next
. next
ejecuta el código y devuelve el resultado del generador .
var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}
ret
es el resultado del generador . Tiene dos propiedades: value
, el valor que se obtiene en la función del generador , y done
, un indicador que indica si la función del generador regresa.
console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}
En este punto, nadie esperará que usted entienda el generador, al menos no la potencia asíncrona del generador.
Para ponerlo simple, el generador tiene dos características:
- uno puede elegir saltar de una función y dejar que el código externo determine cuándo saltar de nuevo a la función.
- el control de la llamada asíncrona se puede hacer fuera de su código
En el código, el yield
salta fuera de la función, y next(val)
vuelve a la función y pasa el valor nuevamente a la función. El código externo puede manejar llamadas asíncronas y decidir el momento adecuado para cambiar a su propio código.
Mira la muestra de nuevo:
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
*/
¿Ver? La función del generador en sí no maneja la devolución de llamada. El código exterior lo hace.
La base está aquí. Puede elaborar este código para admitir la asincronía completa mientras mantiene la función del generador como una sincronización.
Por ejemplo, supongamos que geturl
es una llamada asíncrona que devuelve un objeto de promise
. puede escribir var html = yield getUrl(''www..com'');
Esto salta fuera de tu código. Y el código externo hará cosas como:
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);
});
Para una guía más completa, refiérase a this . Y repositorio como co , galaxy , suspend y etc.