web-dev-qa-db-fra.com

Quel est le mot clé rendement en JavaScript?

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?

208
mck89

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.

80
Matt Ball

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.

179
bishop

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 ...

  • Tout votre code est en retrait d'un niveau dans
  • Vous avez cette fin }) Que vous devez suivre partout
  • Tout cela _ function (err, result) jargon
  • Pas tout à fait clair que vous faites cela pour assigner une valeur à result

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 !

52
Leander

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 .
  • De plus, il est utile de renvoyer une valeur depuis une fonction génératrice .

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 

Essayez-le maintenant

Usages

  • Évaluation paresseuse
  • Séquences infinies
  • Flux de contrôle asynchrones

Références:

46
noelyahan

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)

17
David

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 .

16
Matthew Flaschen

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());
6
Nick Sotiros

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 
4
nikksan

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()
3