Qual è la parola chiave yield in JavaScript?

Ho sentito parlare di una parola chiaveyield” in JavaScript, ma ho trovato una documentazione molto scarsa a riguardo. Qualcuno può spiegarmi (o consigliare un sito che spiega) il suo utilizzo e a cosa serve?

    La documentazione MDN è abbastanza buona, IMO.

    La funzione contenente la parola chiave yield è un generatore. Quando lo chiami, i suoi parametri formali sono legati ad argomenti reali, ma il suo corpo non viene effettivamente valutato. Invece, viene restituito un generatore-iteratore. Ogni chiamata al metodo nexter del generatore-iteratore esegue un altro passaggio attraverso l’algoritmo iterativo. Il valore di ogni passaggio è il valore specificato dalla parola chiave yield. Pensa al rendimento come alla versione generatore-iteratore di ritorno, indicando il confine tra ciascuna iterazione dell’algoritmo. Ogni volta che si chiama next (), il codice del generatore riprende dalla dichiarazione successiva alla resa.

    Risposta tardiva, probabilmente tutti conoscono la yield ora, ma è arrivata una documentazione migliore.

    Adattamento di un esempio tratto da “Javascript’s Future: Generators” di James Long per lo standard ufficiale di Harmony:

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

    “Quando chiami foo, recuperi un object generatore che ha un metodo successivo.”

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

    Quindi la yield è un po ‘come il return : si ottiene qualcosa indietro. return x restituisce il valore di x , ma yield x restituisce una funzione, che fornisce un metodo per iterare verso il valore successivo. Utile se hai una procedura potenzialmente intensiva di memoria che potresti voler interrompere durante l’iterazione.

    Semplificando / elaborando la risposta di Nick Sotiros (che ritengo sia formidabile), penso che sia meglio descrivere come si inizierebbe a codificare con il yield .

    A mio parere, il più grande vantaggio dell’utilizzo di yield è che eliminerà tutti i problemi di callback annidati che vediamo nel codice. È difficile vedere come all’inizio, ed è per questo che ho deciso di scrivere questa risposta (per me stesso, e spero che gli altri!)

    Il modo in cui lo fa è introducendo l’idea di un co-routine, che è una funzione che può interrompersi / sospendere volontariamente fino a ottenere ciò di cui ha bisogno. In javascript, questo è denotato dalla function* . Solo le function* possono utilizzare la yield .

    Ecco alcuni tipici javascript:

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

    Questo è goffo perché ora tutto il tuo codice (che ovviamente deve aspettare questa chiamata loadFromDB ) deve essere all’interno di questo brutto richiamo. Questo è male per alcune ragioni …

    • Tutto il tuo codice è rientrato di un livello in
    • Hai questa fine }) cui hai bisogno per tenere traccia di ovunque
    • Tutto questo gergo di function (err, result) extra function (err, result)
    • Non è esattamente chiaro che lo stai facendo per assegnare un valore al result

    D’altra parte, con la yield , tutto ciò può essere fatto in una sola riga con l’aiuto della buona struttura di co-routine.

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

    E così ora la tua funzione principale arriverà dove è necessario quando è necessario attendere variabili e cose da caricare. Ma ora, per eseguire ciò, è necessario chiamare una funzione normale (non di coroutine). Un semplice framework di co-routine può risolvere questo problema in modo che tutto ciò che devi fare sia eseguire questo:

     start(main()) 

    E la partenza è definita (dalla risposta di 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 }); } } 

    E ora, puoi avere un codice bello che è molto più leggibile, facile da cancellare, e non c’è bisogno di armeggiare con rientri, funzioni, ecc.

    Un’osservazione interessante è che in questo esempio, yield è in realtà solo una parola chiave che puoi mettere prima di una funzione con un callback.

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

    Stamperebbe “Hello World”. In questo modo è ansible trasformare qualsiasi funzione di callback in yield semplicemente la stessa firma di funzione (senza il cb) e restituendo la function (cb) {} , in questo modo:

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

    Speriamo che con questa conoscenza si possa scrivere un codice più pulito, più leggibile e facile da eliminare !

    È davvero semplice, è così che funziona

    • yield keyword semplicemente aiuta a mettere in pausa e riprendere una funzione in qualsiasi momento in modo asincrono .
    • Inoltre aiuta a restituire valore da una funzione generatore .

    Prendi questa semplice funzione di generatore :

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

    Finché non si chiama _process.next () non eseguirà le prime 2 righe di codice, quindi la prima resa interromperà la funzione. Per riprendere la funzione fino al prossimo punto di pausa ( yield keyword ) è necessario chiamare _process.next () .

    Puoi pensare che più rendimenti siano i punti di rottura in un debugger javascript all’interno di una singola funzione. Finché non dirai di navigare nel prossimo punto di interruzione, non eseguirà il blocco di codice. ( Nota : senza bloccare l’intera applicazione)

    Ma mentre yield esegue questa pausa e riprende i comportamenti, può restituire anche alcuni risultati {value: any, done: boolean} base alla funzione precedente non abbiamo emesso alcun valore. Se esploriamo l’output precedente mostrerà lo stesso { value: undefined, done: false } con valore indefinito .

    Consente di scavare nella parola chiave yield. Opzionalmente è ansible aggiungere espressioni e impostare assegnare un valore opzionale predefinito . (Sintassi ufficiale del documento)

     [rv] = yield [expression]; 

    espressione : valore da restituire dalla funzione generatore

     yield any; yield {age: 12}; 

    rv : restituisce il valore facoltativo passato al metodo next () del generatore

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

    Provalo ora

    usi

    • Valutazione pigra
    • Sequenze infinite
    • Flussi di controllo asincroni

    Riferimenti:

    È usato per generatori di iteratori. Fondamentalmente, ti permette di creare una sequenza (potenzialmente infinita) usando un codice procedurale. Vedi la documentazione di Mozilla .

    Per dare una risposta completa: la yield funziona in modo simile al return , ma in un generatore.

    Per quanto riguarda l’esempio comunemente fornito, questo funziona come segue:

     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 

    Ma c'è anche un secondo scopo della parola chiave yield. Può essere utilizzato per inviare valori al generatore.

    Per chiarire, un piccolo esempio:

     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 

    Questo funziona, dato che il valore 2 è assegnato a y , inviandolo al generatore, dopo che si è fermato al primo yield (che ha restituito 0 ).

    Questo ci abilita ad alcune cose davvero funky. (guarda la coroutine)

    yield può anche essere utilizzata per eliminare l’inferno del callback, con un framework di 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()); 

    Generatore di sequenze di Fibonacci usando la parola chiave 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