Usando for wait ... di con iterables sincroni


11

MDN dice che for await...of ha due casi d'uso:

L' for await...ofistruzione crea un ciclo che scorre su oggetti iterabili asincroni e su iterabili di sincronizzazione, ...

In precedenza ero a conoscenza del primo: async iterables using Symbol.asyncIterator. Ma ora sono interessato a quest'ultimo: iterables sincroni.

Il seguente codice scorre su un iterabile sincrono - una serie di promesse. Sembra bloccare il progresso sull'adempimento di ogni promessa.

async function asyncFunction() {
    try {
        const happy = new Promise((resolve)=>setTimeout(()=>resolve('happy'), 1000))
        const sad = new Promise((_,reject)=>setTimeout(()=>reject('sad')))
        const promises = [happy, sad]
        for await(const item of promises) {
            console.log(item)
        }
    } catch (err) {
        console.log(`an error occurred:`, err)
    }
}

asyncFunction() // "happy, an error occurred: sad" (printed in quick succession, after about 5 seconds)

Il comportamento sembra essere simile ad attendere ogni promessa a turno, secondo la logica mostrata di seguito. Questa affermazione è corretta?

Lo chiedo perché questo schema di codice ha una trappola implicita di rifiuto che lo evita Promise.alled Promise.allSettledevita, e mi sembra strano che questo schema sia esplicitamente supportato dal linguaggio.


2
Qual è esattamente la tua domanda? Sembra che gli esempi forniti
abbiano

La mia descrizione di for await... ofiterables sincroni è corretta e, in caso affermativo, importa che quel modello possa emettere errori di rifiuto non gestiti?
Ben Aston

"È corretto" non è una domanda. "Corretto" è qualunque cosa tu dica che sia.
Robert Harvey

Puoi dimostrare tramite codice l'emissione di errori di rifiuto non gestiti che hai descritto?
Robert Harvey,

Il codice finale lo dimostra. Corretto ha un significato ben definito in questo contesto perché ho fornito il codice per descrivere ciò che penso stia facendo. Se il comportamento corrisponde al mio codice, il mio codice è corretto, altrimenti la mia comprensione non è corretta. Anche l'osservazione "corretta" è qualunque cosa tu dica. è chiaramente falso. Corretto ha un significato ben definito in questo contesto.
Ben Aston,

Risposte:


4

Sì, è strano e non dovresti farlo. Non ripetete le matrici di promesse, ciò porta esattamente al problema dei rifiuti non gestiti che lei ha citato .

Quindi perché questo è supportato nella lingua? Per continuare con la semantica promessa sciatta.

Puoi trovare il ragionamento esatto in questo commento del problema che discute questa parte della proposta :

Penso che dovremmo ricorrere a Symbol.iteratorperché la nostra semantica Promise attuale consiste nel permettere alle cose di sincronizzazione di essere usate come cose asincrone. Potresti chiamare questa "sciattezza". Segue la logica di @ acque sotterranee sopra , ma voglio solo precisare i parallelismi in modo più dettagliato.

La semantica "concatenata" di .thentutto questo. Puoi restituire una Promessa da .theno un valore scalare; è tutto uguale. Chiami Promise.resolvenon per avvolgere qualcosa in una Promessa, ma per lanciare qualcosa in una Promessa: ottieni un valore asincrono quando hai qualcosa o altro.

La semantica asynce awaitriguarda anche l'essere sciatti. È possibile dare uno schiaffo awaita qualsiasi espressione non promessa in una funzione asincrona e tutto funziona perfettamente, esattamente allo stesso modo, tranne che si dà il controllo alla coda dei lavori. Allo stesso modo, puoi "difensivamente" mettere async tutto ciò che vuoi, purché tu sia awaitil risultato. Se hai una funzione che restituisce una Promessa - qualunque cosa! puoi farne una asyncfunzione e, dal punto di vista dell'utente, nulla cambia (anche se, tecnicamente, ottieni un altro oggetto Promise).

Gli iteratori e i generatori asincroni dovrebbero funzionare allo stesso modo. Proprio come puoi aspettare un valore che, per sbaglio, non era una Promessa, un utente ragionevole si aspetterebbe di essere in grado di yield*sincronizzare un iteratore all'interno di un generatore asincrono. for awaiti loop dovrebbero allo stesso modo "funzionare" se un utente in modo difensivo contrassegna un loop in quel modo, pensando che forse potrebbero ottenere un iteratore asincrono.

Penso che sarebbe un grosso problema rompere tutti questi parallelismi. Renderebbe gli iteratori asincroni meno ergonomici. Discutiamolo la prossima volta che generatori / iteratori asincroni verranno inseriti nell'agenda del TC39.


Grazie. Viene emesso un evento o in realtà è un altro tipo di errore? Lo chiedo perché pensavo che gli eventi facessero parte del WebAPI. L'emissione degli eventi viene utilizzata in modo simile, in altre parti delle specifiche?
Ben Aston,

@ 52d6c6af Ti riferisci agli unhandledrejectioneventi?
Bergi,

Sì. È solo per intercettare l '"errore" che ho usato window.addEventListener('unhandledrejection',...In breve: è l'unica istanza che posso ricordare, di questo tipo di emissione di errori da JavaScript. Ho quasi certamente sbagliato a pensarlo, comunque. Infine: l'emissione di questo "errore" ha davvero importanza oltre ad avere un messaggio di errore indesiderato nella console?
Ben Aston,

1
@ 52d6c6af Vedi qui e come questo è specificato in uno sforzo congiunto tra le specifiche ECMAScript e API Web. No, l'evento non ha molta importanza, è già troppo tardi quando hai capito. Afaics, viene utilizzato solo per il monitoraggio degli errori sul lato client.
Bergi,

Se non importa davvero, il consiglio è ancora "non iterare array di promesse", o è piuttosto "essere consapevoli del fatto che questo non mostra un comportamento a digiuno in alcune circostanze"?
Ben Aston,

0

La sadpromessa non è essendo awaitndr quando non riesce - che le esigenze di codice per finire in attesa di happyprima che possa cominciare ad aspettare sad. La sadpromessa non riesce prima di happyrisolversi. ( Promise.allÈ uno strumento più adatto a questo caso d'uso)


1
Lo so. Da qui la mia domanda. Se Promise.allè una soluzione migliore, perché il linguaggio soddisfa questa sintassi? for await...ofavrebbe potuto essere facilmente implementato per enumerare semplicemente gli iterable asincroni. Ma hanno provveduto a enumerare iterables sincroni (ma con una trappola (apparente?)). Perché?
Ben Aston,

1
Ah, ho frainteso. Ci stiamo chiedendo perché for await ... ofaccetta iterables sincroni? Immagino di supportare generatori asincroni che condizionatamente potrebbero restituire elementi sincroni.
Gershom il

Sì, soprattutto perché sembra introdurre un trabocchetto di rigetto.
Ben Aston,

A mio avviso, la trappola è più in generale nel creare una promessa e non aspettarla immediatamente. Sfortunatamente questa trappola è spesso anche una funzione molto utile.
Gershom,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.