Programmazione asincrona JavaScript: promesse vs generatori

Promesse e generatori ti permettono di scrivere codice asincrono. Non capisco perché entrambi questi meccanismi siano introdotti nello script ECMA 6. Quando è meglio usare le promesse e quando i generatori?

Non c’è opposizione tra queste due tecniche. Essi coesistono e si completano piacevolmente. Le promesse ti danno la possibilità di ottenere il risultato di un’operazione asincrona che non è ancora disponibile. Risolve il problema di Pyramid of Doom . Quindi, invece di:

function ourImportantFunction(callback) { //... some code 1 task1(function(val1) { //... some code 2 task2(val1, function(val2) { //... some code 3 task3(val2, callback); }); }); } 

tu puoi scrivere:

 function ourImportantFunction() { return Promise.resolve() .then(function() { //... some code 1 return task1(val3) }) .then(function(val2) { //... some code 2 return task2(val2) }) .then(function(val2) { //... some code 3 return task3(val2); }); } ourImportantFunction().then(callback); 

Ma anche con le promesse è necessario scrivere codice in modo asincrono: è necessario passare sempre i callback alle funzioni. Scrivere codice asincrono è molto più difficile che sincronico. Anche con le promesse quando il codice è enorme diventa difficile vedere l’algoritmo (beh, è ​​molto soggettivo, qualcuno può discuterne, ma per la maggior parte dei programmatori penso sia vero). Quindi vogliamo scrivere codice asincrono in modo sincrono. Ecco dove stanno arrivando i generatori per aiutarci. Quindi, invece del codice sopra puoi scrivere:

 var ourImportantFunction = spawn(function*() { //... some code 1 var val1 = yield task1(); //... some code 2 var val2 = yield task2(val1); //... some code 3 var val3 = yield task3(val2); return val3; }); ourImportantFunction().then(callback); 

dove la più semplice realizzazione di spawn può essere qualcosa del tipo:

 function spawn(generator) { return function() { var iter = generator.apply(this, arguments); return Promise.resolve().then(function onValue(lastValue){ var result = iter.next(lastValue); var done = result.done; var value = result.value; if (done) return value; // generator done, resolve promise return Promise.resolve(value).then(onValue, iter.throw.bind(iter)); // repeat }); }; } 

Come puoi vedere il value (risultato di qualche funzione di funzione asincrona task{N} ) deve essere una promise. Non puoi farlo con i callback.

Ciò che resta da fare è implementare la tecnica di spawn nella lingua stessa. Quindi stiamo rimpiazzando spawn con async e produciamo in await e stiamo arrivando ad ES7 async / await :

 var ourImportantFunction = async function() { //... some code 1 var val1 = await task1(); //... some code 2 var val2 = await task2(val1); //... some code 3 var val3 = await task3(val2); return val3; } 

Ti consiglio di guardare questo video per capire meglio questo e altre tecniche in arrivo. Se il ragazzo parla troppo veloce per te, rallenta la velocità di riproduzione (“impostazioni” nell’angolo in basso a destra, o semplicemente premi [ shift + < ])

Qual è il migliore: solo callback, o promesse o promesse con i generatori - questa è una domanda molto soggettiva. Il callback è la soluzione più veloce ansible in questo momento (le prestazioni delle promesse native sono molto scarse ora). Le promesse con i generatori ti danno l'opportunità di scrivere codice asincrono in modo sincrono. Ma per ora sono molto più lenti di semplici callback.

Promesse e generatori sono diversi modelli di software (costrutti):

  1. http://en.wikipedia.org/wiki/Futures_and_promises
  2. http://en.wikipedia.org/wiki/Generator_(computer_programming)

In realtà, i generatori non sono asincroni.

I generatori sono utili quando è necessario ottenere una serie di valori non in una volta, ma uno per domanda. Il generatore restituirà immediatamente il valore successivo (in modo sincrono) ad ogni chiamata finché non raggiunge la fine della sequenza (o infinita in caso di serie infinite).

Le promesse sono utili quando è necessario “rinviare” il valore, che non può essere calcolato (o potrebbe non essere ancora disponibile). Quando il valore è disponibile, è l’intero valore (non parte di esso) anche se è un array o un altro valore complesso.

Puoi vedere maggiori dettagli ed esempi negli articoli di Wikipedia.