2015-04-17 11 views
5

Sto sperimentando con i generatori ES6 con l'aiuto di babel e ho difficoltà a capire come (o se!) Posso utilizzare efficacemente la funzione asincrona basata sulla richiamata per emettere un iteratore.Generatori ES6: trasformazione dei callback agli iteratori

Diciamo che voglio essere in grado di scrivere una funzione che accetta un numero di URL, scaricarli in modo asincrono e restituirli non appena vengono scaricati. Mi piacerebbe essere in grado di scrivere qualcosa di simile a quanto segue:

let urls = ['http://www.google.com', 'http://www.stackoverflow.com' ]; 
for ({url, data} of downloadUrls(urls)) { 
    console.log("Content of url", url, "is"); 
    console.log(data); 
} 

Come posso implementare downloadUrls? Idealmente mi vorrebbe poter scrivere il seguente:

var downloadUrls = function*(urls) { 
    for(let url of urls) { 
     $.ajax(url).done(function(data) { 
      yield data; 
     }); 
    } 
}; 

Questo ovviamente non funziona, poiché `` resa '' viene richiamato all'interno di un callback e non direttamente all'interno del generatore. Sono in grado di trovare molti esempi online di persone che provano lo stesso, sono not much transparent), richiedono enabling browser/node flags o utilizzano funzionalità/librerie specifiche del nodo. La libreria più vicina a ciò di cui ho bisogno sembra essere task.js, ma non riesco ad avere nemmeno l'esempio più semplice eseguito su Chrome corrente.

C'è un modo per ottenere il comportamento desiderato utilizzando le funzionalità standard e attuali, (Con l'attuale intendo utilizzabile con transpiler come babel, ma senza la necessità di abilitare le bandiere aggiuntive sul browser) o devo aspettare per async/await ?

+0

In che modo esattamente le spiegazioni online non sono "trasparenti" per te?L'articolo di David Walsh è uno dei migliori che ho letto (ma è necessario leggere l'intera serie ovviamente) – Bergi

+0

Sembra rilevante: proposta generatore asincrono https://github.com/jhusain/asyncgenerator (non supportato ancora da nessuna parte PER QUANTO NE SO). –

risposta

1

c'è un modo per ottenere il comportamento desiderato utilizzando le funzionalità standard e attuali

Sì, uso promesse e generatori. Molte librerie promettenti, e alcune indipendenti, prevedono l'uso di "coroutine" di generatore.

Ma si noti che you cannot mix iteration with asynchrony, è possibile utilizzare i generatori per solo. Il tuo esempio sembra confonderli un po '- sembra che ti aspetti che il ciclo for ({url, data} of downloadUrls(urls)) { funzioni in modo sincrono, il che non può funzionare.

devo aspettare async/await?

No, non devi aspettare, Babel already supports them!

1

Ecco un modo pulito per utilizzare un generatore/iteratore per appiattire codice asincrono che funziona per me in node.js:

var asyncProcedureGenerator1 = function*() { 
    var it = yield(0); //get a reference to the iterator 
    try { 
     var a = yield (asyncPart1.bind(it))(0); //call the function, set this = it 
     var b = yield (asyncPart2.bind(it))(a); 
     var c = yield (asyncPart3.bind(it))(b); 
     console.log("c = ", c); 
    } 
    catch(err) 
    { 
     console.log("Something went wrong: ", err); 
    } 
}; 

var runAsyncGenerator = function(generator) { 
    var asyncProcedureIterator = generator(); //create an iterator 
    asyncProcedureIterator.next(); //start the iterator 
    asyncProcedureIterator.next(asyncProcedureIterator); //pass a reference of the iterator to itself 
} 

var asyncPart1 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart1 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart1"); 
     var returnValue = 42 + param1; 
     console.log("asyncPart1 returned ", returnValue); 
     it.next(returnValue); //when we are done, resume the iterator which has yielded to us. 
    },2000); 
}; 

var asyncPart2 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart2 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart2"); 
     var returnValue = param1/2; 
     console.log("asyncPart2 returned ", returnValue); 
     //it.throw("Uh oh."); 

     it.next(returnValue); 

    },2000); 
}; 

var asyncPart3 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart3 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart3"); 
     var returnValue = param1/3; 
     console.log("asyncPart3 returned ", returnValue); 
     it.next(returnValue); 
    },2000); 
}; 

runAsyncGenerator(asyncProcedureGenerator1); 

L'idea è quella di eseguire il generatore, creatore un iteratore e quindi passare un riferimento di questo iteratore a se stesso.

Quindi l'iteratore può chiamare funzioni asincrone (con yield) e passargli un riferimento a se stesso che consente a quelle funzioni di restituire esito positivo e riprendere l'esecuzione chiamando iterator.next (risultato) o errore chiamando iterator.throw (errore).

Ho appena creato questo schema, quindi potrebbero esserci alcuni trucchi che non ho ancora trovato, ma sembra funzionare e consente un codice molto piatto con aggiunte minime.

Problemi correlati