2017-01-17 1 views
3

J'ai un morceau de code:compréhension du générateur de Javascript

function * input(){ 
    let array = []; 
    while(true) { 
     array.push(yield array); 
    } 
} 

var gen = input(); 
console.log(gen.next("A")) 
console.log(gen.next("B")) 
console.log(gen.next("C")) 
console.log(gen.next("D")) 

Lorsque vous exécutez vous obtiendrez la sortie suivante:

{ value: [], done: false } 
{ value: [ 'B' ], done: false } 
{ value: [ 'B', 'C' ], done: false } 
{ value: [ 'B', 'C', 'D' ], done: false } 

Pourquoi la première ligne du résultat ne comprend pas A dans le tableau? Il y a une explication de cette page au https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#Passing_arguments_into_Generators. Le commentaire dit

le premier appel de la prochaine() effectue depuis le début de la fonction jusqu'à ce que la première instruction yield

Mais de mon test, il ne semble pas correct. Mon code de test est:

function* logGenerator() { 
    console.log("before yield in function"); 
    yield 1; 
    console.log("filler 1"); 
    yield 2; 
    console.log("filler 2"); 
    yield 3; 
    console.log("filler 3"); 
} 

var gen = logGenerator(); 

console.log(gen.next()); 
console.log("-----------------"); 
console.log(gen.next()); 
console.log("-----------------"); 
console.log(gen.next()); 
console.log("-----------------"); 
console.log(gen.next()); 

Le résultat est:

before yield in function 
{ value: 1, done: false } 
----------------- 
filler 1 
{ value: 2, done: false } 
----------------- 
filler 2 
{ value: 3, done: false } 
----------------- 
filler 3 
{ value: undefined, done: true } 

Comme vous pouvez le voir, la première next() non seulement exécuté les déclarations avant le premier yield, mais aussi la première déclaration yield. Donc cette théorie ne peut pas expliquer ma question. Quelqu'un peut-il aider à me diriger dans la bonne direction? Merci d'avance.

+1

L'opérateur de rendement doit avoir préséance. –

+1

C'est intéressant. en cours d'exécution 'console.log (gen.next());' donne d'abord la sortie attendue –

Répondre

1

Considérons le premier générateur que vous avez écrit réécrit de cette façon.

function * input(){ 
 
    let array = []; 
 
    while(true) { 
 
     var thingToAdd = yield array; 
 
     console.log(thingToAdd); 
 
     array.push(thingToAdd); 
 
    } 
 
} 
 

 
var gen = input(); 
 
console.log(gen.next("A")) 
 
console.log(gen.next("B")) 
 
console.log(gen.next("C")) 
 
console.log(gen.next("D"))

est-il pas plus clair pour voir pourquoi "A" n'est jamais ajouté au tableau? La première exécution du générateur s'arrête à la première déclaration de rendement bien avant que la matrice ne soit modifiée. Au moment où l'exécution revient au générateur, la valeur transmise est "B". La même dynamique se produit dans votre code ici array.push(yield array); Les expressions internes sont évaluées en premier, ainsi le yield arrête l'exécution avant que le push soit accédé.

Je crois que si vous voulez que le générateur respecte la première valeur que vous transmettez, vous devez appeler .next() une fois sans aucun paramètre. Chaque exemple que j'ai vu le fait.

En lisant également la section "Envoyer" de this article, vous pouvez voir votre situation. Notez que ce modèle fonctionne bien pour une interaction de type questions/réponses, car nous ne pouvons pas avoir de réponse avant qu'une question ne soit posée, et tous les appels next suivants passeront dans la réponse à la question précédente et récupéreront la suivante.

var q1 = gen.next(); 
console.log(q1); 
var a = userInput(); 
var q2 = gen.next(a); 
console.log(q2); 
var a2 = userInput(); 
... 
0
function * foo() { 
    var i = 0 
    yield i 

    i += 1 
    yield i 

    i += 1 
    i += 2 
    yield i 
} 

var gen = foo() 
console.log(gen.next()) // 0 
console.log(gen.next()) // 1 
console.log(gen.next()) // 4 

Notez que le var gen = foo() ne fait que créer une instance d'un générateur. C'est la première invocation de .next() qui démarre l'exécution du générateur. Les générateurs s'exécutent jusqu'à ce qu'ils atteignent une instruction yieldet renvoient la valeur de cette instruction yield. À ce stade, le générateur est mis en pause jusqu'à ce qu'une autre invocation de .next() soit effectuée.Par conséquent, tout fonctionne comme prévu dans vos exemples.

Dans le premier exemple, la première instruction yield renvoie le tableau vide. Sur le prochain .next() le tableau est rempli avec la valeur transmise, puis ce tableau est cédé. Dans le code:

function * foo (param) { 
    var array = [] 
    // do nothing with param 
    yield array 

    array.push(param) // 'B' 
    yield array 

    array.push(param) // 'C' 
    yield array 

    array.push(param) // 'D' 
    yield array 
} 

Cela correspond the documentation:

Si une valeur facultative est transmis à la méthode suivante() du générateur, cette valeur devient la valeur retournée par l'opération de rendement courant du générateur.