J'ai entendu parler d'un mot clé "rendement" en JavaScript, mais j'ai trouvé une très mauvaise documentation à ce sujet. Quelqu'un peut-il m'expliquer (ou recommander un site qui explique) son utilisation et à quoi il sert?
Le documentation MDN est très bon, IMO.
La fonction contenant le mot-clé rendement est un générateur. Lorsque vous l'appelez, ses paramètres formels sont liés à des arguments réels, mais son corps n'est pas évalué. Au lieu de cela, un générateur-itérateur est renvoyé. Chaque appel à la méthode next () du générateur-itérateur effectue une autre passe à travers l'algorithme itératif. La valeur de chaque étape est la valeur spécifiée par le mot-clé rendement. Pensez au rendement en tant que version génératrice-itérateur du retour, indiquant la limite entre chaque itération de l'algorithme. Chaque fois que vous appelez next (), le code du générateur reprend à partir de l'instruction qui suit le rendement.
Réponse tardive, tout le monde connaît probablement yield
à présent, mais une meilleure documentation a été fournie.
Adaptant un exemple de "L'avenir de Javascript: Générateurs" par James Long pour le standard officiel Harmony:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
"Lorsque vous appelez foo, vous récupérez un objet Generator qui a une méthode suivante."
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
Donc, yield
est un peu comme return
: vous obtenez quelque chose en retour. return x
renvoie la valeur de x
, mais yield x
renvoie une fonction, qui vous donne une méthode pour itérer vers la valeur suivante. Utile si vous avez une procédure potentiellement très gourmande en mémoire que vous voudrez peut-être interrompre pendant l'itération.
En simplifiant/développant la réponse de Nick Sotiros (ce que je trouve génial), je pense qu'il est préférable de décrire comment commencer à coder avec yield
.
À mon avis, le principal avantage de l'utilisation de yield
est que cela éliminera tous les problèmes de rappel imbriqués que nous voyons dans le code. C'est difficile de voir comment au début, c'est pourquoi j'ai décidé d'écrire cette réponse (pour moi et, espérons-le, pour les autres!)
Pour ce faire, on introduit l’idée d’une routine commune, fonction qui peut arrêter/mettre en pause de façon volontaire jusqu’à ce qu’elle obtienne ce dont elle a besoin. En javascript, cela est noté function*
. Seules les fonctions function*
Peuvent utiliser yield
.
Voici quelques exemples de code javascript:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
C'est maladroit parce que maintenant tout votre code (qui doit évidemment attendre pour cet appel loadFromDB
doit être placé) doit être à l'intérieur de cet appel rappelant moche. C'est mauvais pour plusieurs raisons ...
})
Que vous devez suivre partoutfunction (err, result)
jargonresult
Par contre, avec yield
, tout cela peut être fait en une ligne à l’aide du cadre de co-routine de Nice .
function* main() {
var result = yield loadFromDB('query')
}
Et maintenant, votre fonction principale cédera le cas échéant quand elle devra attendre que des variables et des objets se chargent. Mais maintenant, pour exécuter ceci, vous devez appeler une fonction normale (non-coroutine). Un simple framework de co-routine peut résoudre ce problème, de sorte que tout ce que vous avez à faire est d'exécuter ceci:
start(main())
Et le début est défini (d'après la réponse 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
});
}
}
Et maintenant, vous pouvez avoir un code magnifique, beaucoup plus lisible, facile à supprimer et inutile de jouer avec les retraits, les fonctions, etc.
Une observation intéressante est que, dans cet exemple, yield
n'est en réalité qu'un mot-clé que vous pouvez mettre avant une fonction avec rappel.
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
Souhaitez imprimer "Hello World". Vous pouvez donc transformer n'importe quelle fonction de rappel en utilisant yield
en créant simplement la même signature de fonction (sans le cb) et en retournant function (cb) {}
, comme ceci:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Espérons qu'avec cette connaissance, vous pourrez écrire un code plus lisible et plus lisible que facile à supprimer !
C'est vraiment simple, c'est comme ça que ça marche
yield
mot-clé aide simplement à mettre en pause et à reprendre a fonction à tout moment de manière asynchrone .Prenez cette simple générateur :
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();
let out1 = _process.next();
console.log(out1);
let out2 = _process.next();
console.log(out2);
let out3 = _process.next(90);
console.log(out3);
let _process = process ();
Jusqu'à ce que vous appeliez le _ process.next () it wont Exécute les 2 premières lignes du code, puis le premier résultat mettra en pause la fonction. Pour reprendre la fonction jusqu'à la prochaine pause point ( mot-clé de rendement ) vous devez appeler _ process.next ().
Vous pouvez penser que plusieurs rendements sont les points d'arrêt d'un débogueur JavaScript au sein d'un fonction unique. Tant que vous n’avez pas indiqué de naviguer au point d’arrêt suivant, il n’exécutera pas le bloc de code. ( Remarque : sans bloquer l'ensemble de l'application)
Mais bien que le rendement effectue cette pause et reprenne les comportements, il peut renvoyer des résultats ainsi {value: any, done: boolean}
selon la fonction précédente, nous n’émettons aucune valeur. Si nous explorons la sortie précédente, elle affichera le même { value: undefined, done: false }
avec une valeur non définie .
Permet de creuser le mot-clé de rendement. Vous pouvez éventuellement ajouter expression et définir une valeur optionnelle par défaut . (Syntaxe officielle du document)
[rv] = yield [expression];
expression : valeur à renvoyer depuis la fonction génératrice
yield any;
yield {age: 12};
rv : renvoie la valeur facultative transmise à la méthode next () du générateur.
Vous pouvez simplement passer des paramètres à la fonction process () avec ce mécanisme, pour exécuter différentes parties de rendement.
let val = yield 99;
_process.next(10);
now the val will be 10
Usages
Références:
Pour donner une réponse complète: yield
fonctionne de manière similaire à return
, mais dans un générateur.
En ce qui concerne l'exemple généralement donné, cela fonctionne comme suit:
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
Mais il y a aussi un deuxième objectif du mot-clé rendement. Il peut être utilisé pour envoyer des valeurs au générateur.
Pour clarifier, un petit exemple:
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
Cela fonctionne, comme la valeur 2
est assigné à y
, en l’envoyant au générateur, après s’être arrêté au premier rendement (qui a retourné 0
).
Cela nous permet de faire des choses vraiment géniales. (cherche coroutine)
Il est utilisé pour les générateurs d'itérateurs. En gros, cela vous permet de créer une séquence (potentiellement infinie) à l'aide d'un code de procédure. Voir documentation de Mozilla .
yield
peut également être utilisé pour éliminer l'enfer de rappel, avec un framework coroutine.
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());
Générateur de séquence de Fibonacci utilisant le mot-clé rendement.
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
Dépendance entre les appels javascript async.
Un autre bon exemple de la façon dont le rendement peut être utilisé.
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()