nodejs node iteradores generators generadores functions ecmascript javascript yield keyword

node - yield javascript



¿Cuál es la palabra clave yield en JavaScript? (8)

Generador de secuencias de Fibonacci utilizando la palabra clave yield.

function* fibbonaci(){ var a = -1, b = 1, c; while(1){ c = a + b; a = b; b = c; yield c; } } var fibonacciGenerator = fibbonaci(); fibonacciGenerator.next().value; // 0 fibonacciGenerator.next().value; // 1 fibonacciGenerator.next().value; // 1 fibonacciGenerator.next().value; // 2

Escuché sobre una palabra clave de "rendimiento" en JavaScript, pero encontré documentación muy pobre al respecto. ¿Puede alguien explicarme (o recomendar un sitio que explique) su uso y para qué se utiliza?


La documentación de MDN es bastante buena, IMO.

La función que contiene la palabra clave yield es un generador. Cuando lo llamas, sus parámetros formales están ligados a argumentos reales, pero su cuerpo no es realmente evaluado. En cambio, se devuelve un generador-iterador. Cada llamada al método next () del generador-iterador realiza otra pasada a través del algoritmo iterativo. El valor de cada paso es el valor especificado por la palabra clave yield. Piense en yield como la versión de retorno generador-iterador, que indica el límite entre cada iteración del algoritmo. Cada vez que llame a next (), el código del generador se reanuda a partir de la declaración que sigue al rendimiento.


Para dar una respuesta completa: el yield está funcionando de manera similar al return , pero en un generador.

En cuanto al ejemplo comúnmente dado, esto funciona de la siguiente manera:

function *squareGen(x) { var i; for (i = 0; i < x; i++) { yield i*i; } } var gen = squareGen(3); console.log(gen.next().value); // prints 0 console.log(gen.next().value); // prints 1 console.log(gen.next().value); // prints 4

Pero también hay un segundo propósito de la palabra clave yield. Se puede usar para enviar valores al generador.

Para aclarar, un pequeño ejemplo:

function *sendStuff() { y = yield (0); yield y*y; } var gen = sendStuff(); console.log(gen.next().value); // prints 0 console.log(gen.next(2).value); // prints 4

Esto funciona, ya que el valor 2 se asigna a y , enviándolo al generador, después de que se detuvo en el primer rendimiento (que devolvió 0 ).

Esto nos permite algunas cosas realmente funky. (buscar corutina)


Se usa para generadores de iteradores. Básicamente, le permite hacer una secuencia (potencialmente infinita) usando código de procedimiento. Vea la documentación de Mozilla .


Simplificando / elaborando la respuesta de Nick Sotiros (que creo que es impresionante), creo que es mejor describir cómo se empezaría a codificar con yield .

En mi opinión, la mayor ventaja de usar el yield es que eliminará todos los problemas de devolución de llamada anidados que vemos en el código. Es difícil ver cómo al principio, y es por eso que decidí escribir esta respuesta (¡para mí y, con suerte, para otros!)

La forma en que lo hace es introduciendo la idea de una co-rutina, que es una función que puede parar / pausar voluntariamente hasta que obtenga lo que necesita. En javascript, esto se denota por function* . Solo las function* function pueden usar yield .

Aquí hay algunos javascript típicos:

loadFromDB(''query'', function (err, result) { // Do something with the result or handle the error })

Esto es torpe porque ahora todo su código (que obviamente necesita esperar por esta llamada loadFromDB ) debe estar dentro de esta retrollamada de aspecto feo. Esto es malo por algunas razones ...

  • Todo su código está sangrado un nivel en
  • Tiene este final }) que necesita para realizar un seguimiento de todos lados
  • Toda esta jerga de function (err, result) extra function (err, result)
  • No está exactamente claro que está haciendo esto para asignar un valor al result

Por otro lado, con el yield , todo esto se puede hacer en una línea con la ayuda del buen marco de co-rutina.

function* main() { var result = yield loadFromDB(''query'') }

Y ahora su función principal cederá donde sea necesario cuando necesite esperar a que se carguen las variables y cosas. Pero ahora, para ejecutar esto, necesita llamar a una función normal (no corto). Un marco de co-rutina simple puede solucionar este problema, de modo que todo lo que tiene que hacer es ejecutar esto:

start(main())

Y el comienzo está definido (de la respuesta de Nick Sotiro)

function start(routine, data) { result = routine.next(data); if(!result.done) { result.value(function(err, data) { if(err) routine.throw(err); // continue next iteration of routine with an exception else start(routine, data); // continue next iteration of routine normally }); } }

Y ahora, puede tener un hermoso código que sea mucho más legible, fácil de eliminar y sin necesidad de jugar con sangrías, funciones, etc.

Una observación interesante es que en este ejemplo, el yield es en realidad solo una palabra clave que puede poner antes de una función con una devolución de llamada.

function* main() { console.log(yield function(cb) { cb(null, "Hello World") }) }

Imprimiría "Hello World". De modo que puede convertir cualquier función de devolución de llamada en uso de yield simplemente creando la misma firma de función (sin el cb) y la function (cb) {} retorno function (cb) {} , así:

function yieldAsyncFunc(arg1, arg2) { return function (cb) { realAsyncFunc(arg1, arg2, cb) } }

¡Esperamos que con este conocimiento pueda escribir un código más limpio y legible que sea fácil de eliminar !


Última respuesta, probablemente todo el mundo sabe acerca del yield ahora, pero ha aparecido una mejor documentación.

Adaptando un ejemplo de "Javascript''s Future: Generators" de James Long para el estándar oficial de Harmony:

function * foo(x) { while (true) { x = x * 2; yield x; } }

"Cuando llamas a foo, obtienes un objeto generador que tiene un siguiente método".

var g = foo(2); g.next(); // -> 4 g.next(); // -> 8 g.next(); // -> 16

Entonces el yield es como el return : recuperas algo. return x devuelve el valor de x , pero yield x devuelve una función, que le da un método para iterar hacia el próximo valor. Útil si tiene un procedimiento potencialmente intensivo en memoria que podría querer interrumpir durante la iteración.


yield también se puede utilizar para eliminar el infierno de devolución de llamada, con un marco de corutina.

function start(routine, data) { result = routine.next(data); if(!result.done) { result.value(function(err, data) { if(err) routine.throw(err); // continue next iteration of routine with an exception else start(routine, data); // continue next iteration of routine normally }); } } // with nodejs as ''node --harmony'' fs = require(''fs''); function read(path) { return function(callback) { fs.readFile(path, {encoding:''utf8''}, callback); }; } function* routine() { text = yield read(''/path/to/some/file.txt''); console.log(text); } // with mdn javascript 1.7 http.get = function(url) { return function(callback) { // make xhr request object, // use callback(null, resonseText) on status 200, // or callback(responseText) on status 500 }; }; function* routine() { text = yield http.get(''/path/to/some/file.txt''); console.log(text); } // invoked as.., on both mdn and nodejs start(routine());


Es realmente simple, así es como funciona

  • yield palabra clave yield simplemente ayuda a pausar y reanudar una función en cualquier momento de forma asincrónica .
  • Además, ayuda a devolver el valor de una función del generador .

Tome esta simple función de generador :

function* process() { console.log(''Start process 1''); console.log(''Pause process2 until call next()''); yield; console.log(''Resumed process2''); console.log(''Pause process3 until call next()''); yield; console.log(''Resumed process3''); console.log(''End of the process function''); }

let _process = process ();

Hasta que no llame a _process.next () no ejecutará las primeras 2 líneas de código, entonces el primer rendimiento pausará la función. Para reanudar la función hasta el próximo punto de pausa ( palabra clave yield ), debe llamar a _process.next () .

Puede pensar que los rendimientos múltiples son los puntos de corte en un depurador de JavaScript dentro de una sola función. Hasta que diga navegar el siguiente punto de interrupción, no ejecutará el bloque de código. ( Nota : sin bloquear toda la aplicación)

Pero aunque el rendimiento realiza esta pausa y reanuda los comportamientos, también puede devolver algunos resultados {value: any, done: boolean} acuerdo con la función anterior, no hemos emitido ningún valor. Si exploramos el resultado anterior, se mostrará el mismo { value: undefined, done: false } con el valor undefined .

Vamos a profundizar en la palabra clave yield. Opcionalmente, puede agregar expresión y establecer asignar un valor opcional predeterminado . (Sintaxis del documento oficial)

[rv] = yield [expression];

expresión : valor para devolver desde la función del generador

yield any; yield {age: 12};

rv : devuelve el valor opcional que pasó al método next () del generador

let val = yield 99; _process.next(10); now the val will be 10

Usos

  • Evaluación diferida
  • Secuencias infinitas
  • Flujos de control asincrónico

Referencias