5

ES6 ha generators that return iterators:ES6 risultato generatore asincrono

function* range(n) { 
    for (let i = 0; i < n; ++i) { 
     yield i; 
    } 
} 

for (let x of range(10)) { 
    console.log(x); 
} 

c'è una proposta per le funzioni asincrone che restituiscono Promises:

async function f(x) { 
    let y = await g(x); 
    return y * y; 
} 

f(2).then(y => { 
    console.log(y); 
}); 

Così che cosa succede se combino i due, in questo modo:

async function* ag(n) { 
    for (let i = 0; i < n; ++i) { 
     yield i; 
    } 
} 

Cosa restituisce? È Promise<Iterator<Item>>? Iterator<Promise<Item>>? Qualcos'altro? Come lo consumo? Immagino che ci dovrebbe essere un corrispondente for ciclo, quello che iterare il suo risultato in modo asincrono, qualcosa di simile:

for (await let x of ag(10)) { 
    console.log(x); 
} 

che attende per ogni elemento diventi disponibile prima di tentare di accedere al successivo.

+2

Hmmmm, troppo presto per rispondere a questa domanda, come 'async/proposta await' non è ancora approvato. – thefourtheye

+0

Un iteratore asincrono - c'è una sp c - I'm on mobile ma è già deciso per la maggior parte. È un generatore il cui 'next' restituisce una promessa. –

risposta

5

Promise<Iterator<Item>> o Iterator<Promise<Item>>?

Nessuno dei due. Non è ancora approvato, ma le implementazioni correnti restituiscono qualcos'altro. Kris Kowal ha written an about async generators e riferimenti Jafar Husain's AsyncGenerator proposal for ES7. EDIT: Abbiamo tc39 proposal e babel support!

Definiamo alcuni tipi (semplificato):

interface Iterator<T> { 
    Iteration<T> next(); 
} 

type Iteration<T> = { done: boolean, value: T } 

Siamo alla ricerca di qualcosa che può essere utilizzato in questo modo:

for (;;) { 
    var iteration = await async_iterator.next(); 
    if (iteration.done) { 
     return iteration.value; 
    } else { 
     console.log(iteration.value); 
    } 
} 

Un Iterator<Promise<T>> produce iterazioni sincroni, i cui valori sono promesse. Potrebbe essere utilizzato in questo modo:

for (;;) { 
    var iteration = iterator_promise.next(); 
    if (iteration.done) { 
     return await iteration.value; 
    } else { 
     console.log(await iteration.value); 
    } 
} 

Un Promise<Iterator<T>> è solo un iteratore sincrono regolare, a partire dal futuro:

var iterator = await promise_iterator; 
for (;;) { 
    var iteration = iterator.next(); 
    if (iteration.done) { 
     return iteration.value; 
    } else { 
     console.log(iteration.value); 
    } 
} 

Quindi né Iterator<Promise<T>>Promise<Iterator<T>> era adatto.Attualmente generatori asincroni restituiscono AsyncIterator s invece:

interface AsyncIterator<T> { 
    Promise<Iteration<T>> next(); 
} 

Il che rende perfettamente senso. Passare all'elemento successivo dell'iteratore è l'operazione asincrona e può essere utilizzata esattamente come volevamo.

Come si consumano i generatori asincroni?

Babeljs.io compila già generatori asincroni. Babeljs.io/repl example:

EDIT: No preimpostato su babeljs.io compila generatori asincroni dal babele 6, babel-plugin-transform-regenerator sostiene con {asyncGenerators:true} opzione.

MODIFICA: vedere transform-async-generator-functions plug-in babel 6.

function delay(timeout, val) { 
    return new Promise(resolve => setTimeout(resolve, timeout, val)); 
} 

async function* asyncGenerator() { 
    for (var i = 0; i < 5; i++) { 
    await delay(500); 
    yield i; 
    } 
} 

async function forAwait(iter, fn) { 
    for (;;) { 
    let iteration = await iter.next(); 
    if (iteration.done) return iteration.value; 
    await fn(iteration.value); 
    } 
} 


async function main() { 
    console.log('Started'); 
    await forAwait(asyncGenerator(), async item => { 
    await delay(100); 
    console.log(item); 
    }); 
    console.log('End'); 
} 

main(); 

c'è una proposta per una vantaggiosa for await ciclo per iteratori asincroni (descritto in Async iteration):

for await (let line of readLines(filePath)) { 
    print(line); 
} 

Aggiornamento:

Purtroppo, async-await non è diventato una parte della ECMAScript 2016. Almeno await è menzionata una parola riservata per uso futuro.

Aggiornamento:

proposte correlate:

+0

Ma come "delegare rendimento" a un generatore asincrono? Né "yield * await" né "await yield" * si adatta. –

+0

@ polkovnikov.ph Nella proposta viene menzionato: 'Il comportamento di yield * viene modificato per supportare la delega agli iteratori asincroni. Ma si potrebbe chiedere come si delegherà al generatore regolare? Alla fine puoi sempre usare un rendimento in un ciclo for. –

0

Solo pensando: Le funzioni di Iterator non hanno valore di ritorno, quindi non ha senso renderle asincrone. Poi c'è questo divario concettuale tra questi due approcci. - Gli iteratori sono basati su pull: chiami l'iteratore e invochi il calcolo di un nuovo valore - Le promesse sono basate su push: la Promessa invia un risultato al suo listener. (Una volta o mai più)

E mentre farebbe sence in alcuni casi, per creare un Iterator<Pomise<Item>>

function* f(g){ 
    for(...){ 
     let y = await g(); 
     yield y; 
    } 
} 

non riesco a pensare di qualsiasi caso in cui si avrebbe senso per avvolgere un iteratore in una promessa. dal momento che non c'è nulla di asincrono nell'istanziare un iteratore dalla sua definizione.