Verwenden für for await… von mit synchronen iterables

11

Laut MDN gibt for await...of es zwei Anwendungsfälle:

Die for await...ofAnweisung erstellt eine Schleife, die sowohl über asynchrone iterierbare Objekte als auch über synchronisierte iterierbare Objekte iteriert.

Ersteres war mir vorher bekannt: asynchrone iterables using Symbol.asyncIterator. Aber ich interessiere mich jetzt für Letzteres: synchrone Iterables.

Der folgende Code iteriert über eine synchrone Iterable - eine Reihe von Versprechungen. Es scheint den Fortschritt bei der Erfüllung jedes Versprechens zu blockieren.

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)

Das Verhalten scheint so zu sein, als würde man nach der unten gezeigten Logik nacheinander auf jedes Versprechen warten. Ist diese Behauptung richtig?

Ich frage , weil dieses Muster von Code eine implizite Ablehnung Draht-up pitfall hat , dass Promise.allund zu Promise.allSettledvermeiden, und es scheint mir seltsam , dass dieses Muster explizit von der Sprache unterstützt werden würde.

Ben Aston
quelle
2
Was genau ist deine Frage? Es scheint, als ob die Beispiele, die Sie zur Verfügung gestellt haben, funktionieren
Sagi Rika
Ist meine Beschreibung for await... ofmit synchronen Iterablen korrekt, und wenn ja, spielt es eine Rolle, dass dieses Muster unbehandelte Ablehnungsfehler ausgeben kann?
Ben Aston
"Ist es richtig" ist keine Frage. "Richtig" ist das, was Sie sagen.
Robert Harvey
Können Sie per Code die Ausgabe von unbehandelten Ablehnungsfehlern demonstrieren, die Sie beschrieben haben?
Robert Harvey
Der endgültige Code demonstriert es. Richtig hat in diesem Zusammenhang eine klar definierte Bedeutung, da ich den Code bereitgestellt habe, um zu beschreiben, was ich denke, dass es tut. Wenn das Verhalten mit meinem Code übereinstimmt, ist mein Code korrekt, andernfalls ist mein Verständnis falsch. Auch die Beobachtung "Richtig" ist das, was Sie sagen. ist eindeutig falsch. Richtig hat in diesem Zusammenhang eine klar definierte Bedeutung.
Ben Aston

Antworten:

4

Ja, es ist seltsam und du solltest das nicht tun. Wiederholen Sie keine Arrays von Versprechungen, es führt genau zu dem von Ihnen erwähnten Problem der unbehandelten Ablehnung .

Warum wird dies in der Sprache unterstützt? Fortsetzung der schlampigen Versprechenssemantik.

Die genaue Begründung finden Sie in diesem Kommentar zu diesem Thema des Vorschlags :

Ich denke, wir sollten darauf zurückgreifen, Symbol.iteratorweil es in unserer aktuellen Promise-Semantik darum geht, Synchronisierungssachen als asynchrone Objekte zu verwenden. Man könnte das "Schlamperei" nennen. Es folgt der obigen Logik von @ Grundwasser , aber ich möchte nur die Parallelen genauer darlegen.

Die "Verkettungs" -Semantik von dreht .thensich alles darum. Sie können ein Versprechen von .thenoder einen Skalarwert zurückgeben. Es ist alles das Gleiche. Sie rufen an, Promise.resolvenicht etwas in ein Versprechen zu verpacken, sondern etwas in ein Versprechen umzuwandeln - erhalten Sie einen asynchronen Wert, wenn Sie etwas oder etwas anderes haben.

In der Semantik von asyncund geht awaites auch darum, schlampig zu sein. Sie können awaitin einer asynchronen Funktion auf jeden Nicht-Promise-Ausdruck klatschen, und alles funktioniert einwandfrei, genauso wie Sie die Jobwarteschlange steuern. Ebenso können Sie "defensiv" herumstellen, async was Sie wollen, solange Sie awaitdas Ergebnis haben. Wenn Sie eine Funktion haben, die ein Versprechen zurückgibt - was auch immer! Sie können dies zu einer asyncFunktion machen, und aus Benutzersicht ändert sich nichts (auch wenn Sie technisch gesehen ein anderes Promise-Objekt herausbringen).

Asynchrone Iteratoren und Generatoren sollten auf die gleiche Weise funktionieren. So wie Sie auf einen Wert warten können, der aus Versehen kein Versprechen war, würde ein vernünftiger Benutzer erwarten, dass er yield*einen Synchronisationsiterator in einem asynchronen Generator verwenden kann. for awaitSchleifen sollten in ähnlicher Weise "nur funktionieren", wenn ein Benutzer eine Schleife auf diese Weise defensiv markiert und denkt, dass er möglicherweise einen asynchronen Iterator erhält.

Ich denke, es wäre eine große Sache, all diese Parallelen zu brechen. Dies würde asynchrone Iteratoren weniger ergonomisch machen. Lassen Sie uns dies das nächste Mal diskutieren, wenn asynchrone Generatoren / Iteratoren auf der Tagesordnung von TC39 stehen.

Bergi
quelle
Vielen Dank. Wird ein Ereignis ausgegeben oder handelt es sich tatsächlich um einen anderen Fehler? Ich frage, weil ich dachte, Ereignisse seien Teil der WebAPI. Wird die Emission von Ereignissen in anderen Teilen der Spezifikation auf ähnliche Weise verwendet?
Ben Aston
@ 52d6c6af Beziehen Sie sich auf die unhandledrejectionEreignisse?
Bergi
Ja. Es ist nur das, um den "Fehler" abzufangen, den ich verwendet habe. window.addEventListener('unhandledrejection',...Kurz gesagt: Es ist die einzige Instanz, an die ich mich erinnern kann, diese Art der Fehleremission durch JavaScript. Ich bin jedoch mit ziemlicher Sicherheit falsch, dies zu denken. Schließlich: Ist die Emission dieses "Fehlers" jemals wirklich wichtig, außer dass eine unerwünschte Fehlermeldung in der Konsole angezeigt wird?
Ben Aston
1
@ 52d6c6af Hier und da erfahren Sie , wie dies in einer gemeinsamen Anstrengung zwischen ECMAScript- und Web-API-Spezifikationen angegeben wird. Nein, die Veranstaltung spielt keine Rolle, es ist schon zu spät, als du das hast. Afaics wird nur zur Überwachung clientseitiger Fehler verwendet.
Bergi
Wenn es nicht wirklich wichtig ist, lautet der Rat dann immer noch "Arrays von Versprechungen nicht iterieren" oder eher "sich bewusst sein, dass dies unter bestimmten Umständen kein ausfallsicheres Verhalten zeigt"?
Ben Aston
0

Das sadVersprechen wird nicht sein awaited , wenn es ausfällt - das Code Bedürfnis wartet auf beenden , happybevor sie beginnen können , auf warten sad. Das sadVersprechen scheitert, bevor es happygelöst wird. ( Promise.allist ein Tool, das für diesen Anwendungsfall besser geeignet ist)

Gershom
quelle
1
Ich kenne. Daher meine Frage. Wenn dies Promise.alleine bessere Lösung ist, warum berücksichtigt die Sprache diese Syntax? for await...ofhätte leicht implementiert werden können, um einfach asynchrone Iterables aufzulisten. Aber sie sorgten dafür, dass synchrone Iterables aufgezählt wurden (aber mit einer (scheinbaren?) Falle). Warum?
Ben Aston
1
Ah, ich habe es falsch verstanden. Fragen wir uns, warum for await ... ofsynchrone Iterables akzeptiert werden? Ich würde mir vorstellen, asynchrone Generatoren zu unterstützen, die unter bestimmten Bedingungen synchrone Elemente zurückgeben können.
Gershom
Ja, vor allem angesichts der Tatsache, dass es sich anscheinend um eine Ablehnungsfalle handelt.
Ben Aston
Meiner Meinung nach besteht die Gefahr im Allgemeinen darin, ein Versprechen zu schaffen und es nicht sofort abzuwarten. Leider ist diese Falle auch oft ein sehr nützliches Merkmal.
Gershom