Escuché sobre una palabra clave de "rendimiento" en JavaScript, pero encontré muy poca documentación al respecto. ¿Alguien puede explicarme (o recomendar un sitio que explique) su uso y para qué se utiliza?
Escuché sobre una palabra clave de "rendimiento" en JavaScript, pero encontré muy poca documentación al respecto. ¿Alguien puede explicarme (o recomendar un sitio que explique) su uso y para qué se utiliza?
Respuestas:
La documentación de MDN es bastante buena, en mi opinión.
La función que contiene la palabra clave de rendimiento es un generador. Cuando lo llama, sus parámetros formales están vinculados a argumentos reales, pero su cuerpo no se evalúa realmente. 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 de rendimiento. Piense en el rendimiento como la versión de retorno generador-iterador, que indica el límite entre cada iteración del algoritmo. Cada vez que llama a next (), el código del generador se reanuda a partir de la declaración que sigue al rendimiento.
Respuesta tardía, probablemente todo el mundo lo sepa 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 Generator que tiene un siguiente método".
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
Entonces yield
es algo así como return
: recuperas algo. return x
devuelve el valor de x
, pero yield x
devuelve una función, que le brinda un método para iterar hacia el siguiente valor. Es útil si tiene un procedimiento potencialmente intensivo en memoria que puede querer interrumpir durante la iteración.
function* foo(x){
allí
*
. Si lo necesita o no, depende del tipo de futuro que regrese. El detalle es largo: GvR lo explica para la implementación de Python , sobre el cual se modela la implementación de Javascript. El uso function *
siempre será correcto, aunque en algunos casos un poco más sobrecargado que function
con yield
.
function *
y yield
, y agregó el error citado ("Se genera un error temprano si se produce una expresión de rendimiento o rendimiento * en una función no generadora"). Pero, la implementación original de Javascript 1.7 en Firefox no requería el*
. Respuesta actualizada en consecuencia. ¡Gracias!
Es realmente simple, así es como funciona
yield
La palabra clave simplemente ayuda a pausar y reanudar una función en cualquier momento de forma asincrónica .Tome esta sencilla 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()');
let parms = yield {age: 12};
console.log("Passed by final process next(90): " + parms);
console.log('Resumed process3');
console.log('End of the process function');
}
let _process = process ();
Hasta que llame la _process.next () se planteo ejecutar las 2 primeras líneas de código, a continuación, la primera cosecha será pausar la función. Para reanudar la función hasta el siguiente punto de pausa ( palabra clave de rendimiento ), debe llamar a _process.next () .
Puede pensar que los rendimientos múltiples son los puntos de interrupción en un depurador de JavaScript dentro de una sola función. Hasta que le indique que navegue por el siguiente punto de interrupción, no ejecutará el bloque de código. ( Nota : sin bloquear toda la aplicación)
Pero mientras que los comportamientos de rendimiento Realiza esta pausa y reanudar puede devolver algunos resultados así {value: any, done: boolean}
de acuerdo a la función anterior no hemos emitir cualquier valor. Si exploramos la salida anterior, mostrará lo mismo { value: undefined, done: false }
con un valor indefinido .
Vamos a profundizar en la palabra clave de rendimiento. Opcionalmente, puede agregar expresión y establecer asignar un valor opcional predeterminado . (Sintaxis oficial del documento)
[rv] = yield [expression];
expresión : Valor a devolver de la función generadora
yield any;
yield {age: 12};
rv : devuelve el valor opcional que pasó al método next () del generador
Simplemente puede pasar parámetros a la función process () con este mecanismo, para ejecutar diferentes partes de rendimiento.
let val = yield 99;
_process.next(10);
now the val will be 10
Usos
Referencias
Simplificando / elaborando la respuesta de Nick Sotiros (que creo que es increíble), creo que es mejor describir cómo comenzaría a codificar yield
.
En mi opinión, la mayor ventaja de usar yield
es que eliminará todos los problemas de devolución de llamada anidados que vemos en el código. Al principio es difícil ver cómo, por eso decidí escribir esta respuesta (¡para mí y, con suerte, para los demás!)
La forma en que lo hace es introduciendo la idea de una co-rutina, que es una función que puede detener / pausar voluntariamente hasta que obtenga lo que necesita. En javascript, esto se denota por function*
. Solo las function*
funciones 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 esta loadFromDB
llamada) debe estar dentro de esta devolución de llamada fea. Esto es malo por algunas razones ...
})
que debes seguir en todas partesfunction (err, result)
jerga extraresult
Por otro lado, con yield
todo esto se puede hacer en una línea con la ayuda del agradable marco de co-rutina.
function* main() {
var result = yield loadFromDB('query')
}
Y ahora, su función principal cederá cuando sea necesario cuando necesite esperar a que se carguen las variables y las cosas. Pero ahora, para ejecutar esto, debe llamar a una función normal (no corutina). 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 inicio 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 código hermoso que es mucho más legible, fácil de eliminar y no necesita manipular sangrías, funciones, etc.
Una observación interesante es que, en este ejemplo, en yield
realidad es 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 "Hola Mundo". Por lo tanto, puede convertir cualquier función de devolución de llamada en yield
simplemente creando la misma firma de función (sin el cb) y regresando function (cb) {}
, de esta manera:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Espero que con este conocimiento pueda escribir un código más limpio y legible que sea fácil de eliminar .
function*
es solo una función regular sin rendimiento?
function *
es una función que contiene rendimiento. Es una función especial llamada generador.
yield
todas partes, estoy seguro de que esto tiene más sentido que las devoluciones de llamada, pero no veo cómo esto es más legible que las devoluciones de llamada.
Para dar una respuesta completa: yield
está funcionando de manera similar 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 de rendimiento. 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 2
se le asigna el valor y
, enviándolo al generador, después de que se detuvo en el primer rendimiento (que regresó 0
).
Esto nos permite hacer algunas cosas realmente funky. (buscar corutina)
Se usa para generadores iteradores. Básicamente, le permite hacer una secuencia (potencialmente infinita) usando código de procedimiento. Ver la documentación de Mozilla .
yield
También se puede utilizar para eliminar el infierno de devolución de llamada, con un marco de trabajo de rutina.
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());
Generador de secuencia de Fibonacci usando la palabra clave de rendimiento.
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
Yeild
la palabra clave en la función javaScript lo hace generador,
¿Qué es el generador en JavaScript?
Un generador es una función que produce una secuencia de resultados en lugar de un solo valor, es decir, genera una serie de valores
Lo que significa que los generadores nos ayudan a trabajar de forma asíncrona con los iteradores de ayuda. ¿Ahora qué son los iteradores de pirateo? ¿De Verdad?
Los iteradores son medios a través de los cuales podemos acceder a los elementos uno a la vez
¿desde dónde el iterador nos ayuda a acceder al elemento uno a la vez? nos ayuda a acceder a los elementos a través de las funciones del generador,
las funciones generadoras son aquellas en las que usamos yeild
palabras clave, la palabra clave de rendimiento nos ayuda a pausar y reanudar la ejecución de la función
Aquí hay un ejemplo rápido
function *getMeDrink() {
let question1 = yield 'soda or beer' // execution will pause here because of yield
if (question1 == 'soda') {
return 'here you get your soda'
}
if (question1 == 'beer') {
let question2 = yield 'Whats your age' // execution will pause here because of yield
if (question2 > 18) {
return "ok you are eligible for it"
} else {
return 'Shhhh!!!!'
}
}
}
let _getMeDrink = getMeDrink() // initialize it
_getMeDrink.next().value // "soda or beer"
_getMeDrink.next('beer').value // "Whats your age"
_getMeDrink.next('20').value // "ok you are eligible for it"
_getMeDrink.next().value // undefined
déjame explicar brevemente lo que está pasando
notó que la ejecución se detiene en cada yeild
palabra clave y podemos acceder primero yield
con la ayuda del iterador.next()
esto itera a todas las yield
palabras clave una a la vez y luego regresa indefinido cuando no yield
quedan más palabras clave en palabras simples, puede decir que la yield
palabra clave es el punto de interrupción donde la función se detiene cada vez y solo se reanuda cuando se llama usando el iterador
para nuestro caso: _getMeDrink.next()
este es un ejemplo de iterador que nos está ayudando a acceder a cada punto de interrupción en la función
Ejemplo de generadores:
async/await
si ves la implementación de async/await
verás generator functions & promises
se utilizan para hacer el async/await
trabajo
por favor señale cualquier sugerencia es bienvenida
Dependencia entre llamadas async javascript.
Otro buen ejemplo de cómo se puede usar el rendimiento.
function request(url) {
axios.get(url).then((reponse) => {
it.next(response);
})
}
function* main() {
const result1 = yield request('http://some.api.com' );
const result2 = yield request('http://some.otherapi?id=' + result1.id );
console.log('Your response is: ' + result2.value);
}
var it = main();
it.next()
Antes de aprender sobre el rendimiento, necesita saber sobre los generadores. Los generadores se crean utilizando la function*
sintaxis. Las funciones del generador no ejecutan código, sino que devuelve un tipo de iterador llamado generador. Cuando se da un valor usando el next
método, la función de generador se sigue ejecutando hasta que se encuentra con una palabra clave de rendimiento. El uso yield
le devuelve un objeto que contiene dos valores, uno es valor y el otro está hecho (booleano). El valor puede ser una matriz, un objeto, etc.
Un simple ejemplo:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
También estoy tratando de entender la palabra clave de rendimiento. Según mi comprensión actual, en el generador, la palabra clave de rendimiento funciona como un cambio de contexto de CPU. Cuando se ejecuta la declaración de rendimiento, se guardan todos los estados (por ejemplo, variables locales).
Además de esto, se devolverá un objeto de resultado directo a la persona que llama, como {valor: 0, hecho: falso}. La persona que llama puede usar este objeto de resultado para decidir si 'reactiva' el generador nuevamente llamando a next () (llamar a next () es repetir la ejecución).
Otra cosa importante es que puede establecer un valor en una variable local. La persona que llama 'next ()' puede pasar este valor al 'despertar' el generador. por ejemplo, it.next ('valueToPass'), como este: "resultValue = yield slowQuery (1);" Al igual que cuando se activa una próxima ejecución, la persona que llama puede inyectar algún resultado en ejecución a la ejecución (inyectándolo en la variable local). Por lo tanto, para esta ejecución, hay dos tipos de estado:
El contexto que se guardó en la última ejecución.
Los valores inyectados por el disparador de esta ejecución.
Entonces, con esta característica, el generador puede ordenar múltiples operaciones asíncronas. El resultado de la primera consulta asincrónica se pasará a la segunda configurando la variable local (resultValue en el ejemplo anterior). La segunda consulta asíncrona solo puede ser activada por la respuesta de la primera consulta asíncrona. Luego, la segunda consulta asíncrona puede verificar el valor de la variable local para decidir los próximos pasos porque la variable local es un valor inyectado de la respuesta de la primera consulta.
Las dificultades de las consultas asíncronas son:
devolución de llamada infierno
perder de contexto a menos que los pase como parámetros en la devolución de llamada.
el rendimiento y el generador pueden ayudar en ambos.
Sin rendimiento y generador, para ordenar múltiples consultas asíncronas requiere devolución de llamada anidada con parámetros como contexto que no es fácil de leer y mantener.
A continuación se muestra un ejemplo de consultas asíncronas encadenadas que se ejecuta con nodejs:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
it.next(1);
})
.catch(function (error) {
it.next(0);
})
}
function* myGen(i=0) {
let queryResult = 0;
console.log("query1", queryResult);
queryResult = yield slowQuery('https://google.com');
if(queryResult == 1) {
console.log("query2", queryResult);
//change it to the correct url and run again.
queryResult = yield slowQuery('https://1111111111google.com');
}
if(queryResult == 1) {
console.log("query3", queryResult);
queryResult = yield slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
queryResult = yield slowQuery('https://google.com');
}
}
console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");
A continuación se muestra el resultado de ejecución:
+++++++++++ start +++++++++++
consulta1 0
+++++++++++ end +++++++++++
query2 1
consulta4 0
El siguiente patrón de estado puede hacer lo mismo para el ejemplo anterior:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
sm.next(1);
})
.catch(function (error) {
sm.next(0);
})
}
class StateMachine {
constructor () {
this.handler = handlerA;
this.next = (result = 1) => this.handler(this, result);
}
}
const handlerA = (sm, result) => {
const queryResult = result; //similar with generator injection
console.log("query1", queryResult);
slowQuery('https://google.com');
sm.handler = handlerB; //similar with yield;
};
const handlerB = (sm, result) => {
const queryResult = result; //similar with generator injection
if(queryResult == 1) {
console.log("query2", queryResult);
slowQuery('https://1111111111google.com');
}
sm.handler = handlerC; //similar with yield;
};
const handlerC = (sm, result) => {
const queryResult = result; //similar with generator injection;
if (result == 1 ) {
console.log("query3", queryResult);
slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
slowQuery('https://google.com');
}
sm.handler = handlerEnd; //similar with yield;
};
const handlerEnd = (sm, result) => {};
console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");
El siguiente es el resultado de ejecución:
+++++++++++ start +++++++++++
consulta1 0
+++++++++++ end +++++++++++
query2 1
consulta4 0
no olvide la útil sintaxis 'x of generator' para recorrer el generador. No es necesario utilizar la función next () en absoluto.
function* square(x){
for(i=0;i<100;i++){
x = x * 2;
yield x;
}
}
var gen = square(2);
for(x of gen){
console.log(x);
}