Node.js Generators & Confronta con richiamate
โก Riepilogo intelligente
GeneratorIn Node.js, le funzioni `yield` sono funzioni la cui esecuzione puรฒ essere sospesa e ripresa a richiesta. La parola chiave `yield` mette in pausa la funzione, il metodo `next` la riprende, e questa combinazione semplifica le callback annidate in un codice sequenziale piรน leggibile.

In questa guida esamineremo i generatori e le differenze rispetto alle callback.
Cosa sono i generatori?
Generators divenne popolare in Node.js per via di ciรฒ che sono capaci di fare.
- Generatorsono esecuzioni di funzioni che possono essere sospese e riprese in un secondo momento.
- GeneratorLe s sono utili per concetti come "esecuzione pigra". Sospendendo l'esecuzione e riprendendola a piacimento, i valori vengono prelevati solo quando sono necessari.
Generators hanno i due metodi principali descritti di seguito.
- Metodo di rendimento โ chiamata all'interno di una funzione per interrompere l'esecuzione esattamente sulla riga in cui
yieldappare. - Metodo successivo โ chiamata dall'applicazione principale per riprendere una funzione che ha ceduto il controllo. L'esecuzione continua fino al successivo
yield, o fino alla fine della funzione.
Vediamo un esempio di come si possono usare i generatori. L'esempio definisce una semplice funzione Add che somma due numeri, interrompendo l'esecuzione in diversi punti per mostrare come si comportano i generatori.
function* Add(x) { yield x + 1; // The value passed into the second next() call lands here var y = yield(null); return x + y; } var gen = Add(5); gen.next(); // runs to the first yield, returns { value: 6, done: false } gen.next(6); // resumes, y becomes 6, returns { value: 11, done: true }
Code Spiegazione
- Il primo passo รจ definire la funzione generatrice. Notare la
*aggiunto alfunctionparola chiave. La funzione si chiamaAdde accetta un parametrox. - Migliori
yieldLa parola chiave รจ specifica dei generatori e mette in pausa la funzione a metร esecuzione. L'esecuzione si interrompe qui fino a quandonext()viene invocato. - La seconda
yieldentrambi mettono in pausa la funzione e ricevono un valore: qualunque cosa venga passata al seguentenext()la chiamata diventa il valore diy. - chiamata
gen.next()la prima volta esegue la funzione fino al primoyield. chiamatagen.next(6)lo riprende, assegna 6 ay, e restituiscex + y.
โ ๏ธ Correzione: l'elenco originale conteneva la riga y = 6 subito dopo var y = yield(null);Tale assegnazione sovrascrive qualsiasi valore next() forniture, il che vanifica lo scopo dell'esempio e rende il secondo yield senza senso. Il valore viene ora trasmesso attraverso gen.next(6), che รจ il modo in cui un generatore riceve effettivamente i dati dal chiamante.
Richiamate vs generatori
Generators risolvono il problema noto come callback hell. Le funzioni di callback a volte diventano cosรฌ annidate in profonditร durante lo sviluppo di un'applicazione Node.js che risultano difficili da seguire.
ร qui che entrano in gioco i generatori. Uno degli esempi piรน comuni รจ la creazione di funzioni timer. L'esempio seguente crea una semplice funzione di ritardo e la richiama con ritardi di 1000, 2000 e 3000 ms.
Passo 1) Definisci la funzione di callback con il codice di ritardo necessario.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Spiegazione
- Una funzione chiamata
Timedelayviene creato con un parametroptime, che indica il numero di millisecondi di pausa. setTimeoutattende quel numero di millisecondi e poi richiama la funzione di callback fornita.
โ ๏ธ Correzione: il codice originale รจ passato time come secondo argomento a setTimeout, ma nessuna variabile denominata time esiste โ il parametro รจ ptime. Cosรฌ come รจ scritto, lancia ReferenceError: time is not defined.
Passo 2) Consideriamo ora il codice che utilizza le callback. Per concatenare ritardi di 1000, 2000 e 3000 millisecondi, le callback si annidano l'una nell'altra.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Spiegazione
Timedelayviene chiamata come funzione di callback con il valore 1000.- All'interno di quella chiamata,
Timedelayviene richiamato con 2000. - Dentro quello,
Timedelayviene chiamata una terza volta con 3000.
โ ๏ธ Correzione: ogni callback nel log originale msg, ma il parametro รจ denominato messageTutte e tre le linee hanno lanciato ReferenceError: msg is not defined.
Dal codice qui sopra si puรฒ notare che la situazione si complica con ogni chiamata aggiuntiva: questo annidamento รจ ciรฒ che viene descritto come "callback hell".
Passo 3) Ora osserviamo lo stesso comportamento rappresentato con i generatori. La sequenza si legge come tre linee piatte anzichรฉ come tre livelli di annidamento.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Spiegazione
- ร definita una funzione generatrice per chiamare la
Timedelayfunzione. yieldรจ usato conTimedelaye 1000 come valore del parametro.yieldviene utilizzato di nuovo con 2000 e ancora una volta con 3000.
โ ๏ธ Correzione: l'originale ha scritto console,log(...) con una virgola al posto del punto su tutte e tre le righe, il che costituisce un errore di sintassi e impedisce completamente l'analisi del file.
Come eseguire un Generator con funzione di corsa
Il generatore qui sopra รจ ben fatto, ma non farร nulla da solo. Questo รจ il dettaglio che la maggior parte delle spiegazioni omette, ed รจ il motivo per cui la versione piatta puรฒ sembrare magica. Un generatore รจ una macchina in pausa e qualcosa deve continuare a premere il pulsante. Chiamando Messages() Si limita a creare l'oggetto generatore e a restituire immediatamente il risultato; non si avvia alcun timer e non viene stampato nulla.
Il pezzo mancante รจ un runner: una piccola funzione che chiama next(), attende che il lavoro asincrono termini e poi chiama next() di nuovo con il risultato.
// Timedelay now hands its result back to the runner function Timedelay(ptime) { return function(resume) { setTimeout(function() { resume("Pausing for " + ptime); }, ptime); }; } // The runner drives the generator to completion function run(generatorFunction) { var it = generatorFunction(); function step(value) { var result = it.next(value); if (result.done) return; // result.value is the function returned by Timedelay result.value(step); } step(); } run(Messages);
Tre punti fanno sรฌ che questo funzioni. Primo, Timedelay non accetta piรน direttamente una callback; restituisce una funzione che il runner puรฒ invocare, restituendo il controllo quando il timer si attiva. In secondo luogo, it.next(value) alimenta il risultato precedente nel generatore in pausa, che รจ ciรฒ che permette yield per apparire sul lato destro di un incarico. Terzo, il corridore si ferma non appena result.done diventa vero, quindi la ricorsione termina naturalmente. Questo modello di runner รจ esattamente ciรฒ che librerie come co hanno fornito, ed รจ il meccanismo che async/await successivamente assorbito nella lingua stessa.
Generators vs async/await: quale usare?
Generatorprima erano il modo standard per scrivere codice asincrono piatto async/await esistevano e i due condividono la stessa idea di base di mettere in pausa una funzione a metร . Node ha supportato async/await nativamente dalla versione 7.6, il che modifica la raccomandazione per chiunque inizi un nuovo lavoro oggi.
| Aspetto | Generators | asincrono / attendono |
|---|---|---|
| Runner cercasi | Sรฌ, scritto da te o da una biblioteca | No, integrato nel linguaggio |
| Pausa parola chiave | yield |
await |
| Gestione degli errori | Il corridore deve inoltrare gli errori | Prova e prendi |
| ร possibile interrompere parzialmente | Sรฌ, e puรฒ essere ripreso in modo selettivo | No, corre fino al completamento |
| migliori per | Sequenze pigre e flussi infiniti | Lavoro asincrono ordinario |
Usa il async/await Per il flusso asincrono nel nuovo codice, poichรฉ rimuove il runner e ripristina il normale try-catch. Riservate i generatori a ciรฒ che solo loro sanno fare bene: produrre valori in modo lazy, iterare sequenze grandi o infinite e in qualsiasi caso in cui il consumatore decida esattamente quando viene calcolato il valore successivo.
Uncommon Generator Errori e come risolverli
La maggior parte dei problemi dei generatori deriva dal dimenticare che la funzione รจ inerte finchรฉ qualcosa non la chiama next(), o da piccoli errori di sintassi intorno all'asterisco. Ogni sintomo riportato di seguito ne indica la causa e la soluzione.
- Non succede nulla quando si richiama il generatore: La chiamata restituisce un iteratore, non un risultato. Chiama
next(), oppure passarlo a un corridore. - Errore di sintassi su yield: L'asterisco รจ mancante.
yieldรจ valido solo all'interno di unfunction*. - Il valore restituito da next() risulta indefinito: il primo
next()la chiamata non puรฒ fornire un valore, perchรฉ il generatore non ha raggiunto unyieldancora. Invia i dati dalla seconda chiamata in poi. - Ciclo infinito in un gioco di corsa:
result.donenon viene mai controllato, quindinext()Viene chiamata ripetutamente anche dopo la fine. Il metodo Always Return When Done รจ true. - Gli errori scompaiono silenziosamente: un rifiuto all'interno del passaggio asincrono non viene mai passato al generatore. Inoltralo con
it.throw(err)in modo che un blocco try-catch scritto all'interno del generatore possa effettivamente vederlo e gestirlo.




