Utilisation de l'attente… ou des itérables synchrones

11

MDN dit qu'il for await...of a deux cas d'utilisation:

L' for await...ofinstruction crée une boucle itérative sur des objets itérables asynchrones ainsi que sur des itérables de synchronisation, ...

J'étais auparavant au courant de l'ancien: les itérables asynchrones utilisant Symbol.asyncIterator. Mais je m'intéresse maintenant à ce dernier: les itérables synchrones.

Le code suivant itère sur un itérable synchrone - un tableau de promesses. Il semble bloquer le progrès sur la réalisation de chaque promesse.

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)

Le comportement semble être semblable à l'attente de chaque promesse tour à tour, selon la logique ci-dessous. Cette affirmation est-elle correcte?

Je demande parce que ce modèle de code a un rejet implicite de fil piège-up Promise.allet Promise.allSettledéviter, et il me semble étrange que ce modèle serait soutenu explicitement par la langue.

Ben Aston
la source
2
Quelle est exactement ta question? Il semble que les exemples que vous avez fournis fonctionnent
Sagi Rika
Ma description d' for await... ofitérables synchrones est-elle correcte et, dans l'affirmative, est-il important que ce modèle puisse émettre des erreurs de rejet non gérées?
Ben Aston
"Est-ce correct" n'est pas une question. "Correct" est ce que vous dites.
Robert Harvey
Pouvez-vous démontrer via le code l'émission d'erreurs de rejet non gérées que vous avez décrites?
Robert Harvey
Le code final le démontre. Correct a une signification bien définie dans ce contexte car j'ai fourni le code pour décrire ce que je pense qu'il fait. Si le comportement correspond à mon code, alors mon code est correct, sinon ma compréhension est incorrecte. De plus, l'observation "Correct" est tout ce que vous dites. est clairement faux. Correct a une signification bien définie dans ce contexte.
Ben Aston

Réponses:

4

Oui, c'est étrange, et vous ne devriez pas faire ça. Ne parcourez pas les tableaux de promesses, cela conduit exactement au problème de rejet non géré que vous avez mentionné .

Alors pourquoi est-ce pris en charge dans la langue? Pour continuer avec la sémantique de la promesse bâclée.

Vous pouvez trouver le raisonnement exact dans ce commentaire de la question discutant cette partie de la proposition :

Je pense que nous devrions y revenir Symbol.iteratorparce que notre sémantique actuelle de Promise vise à permettre aux choses de synchronisation d'être utilisées comme des choses asynchrones. Vous pourriez appeler cela "négligence". Il suit la logique de @ groundwater ci - dessus , mais je veux juste préciser les parallèles plus en détail.

La sémantique de "chaînage" de .thentout cela. Vous pouvez renvoyer une promesse de .thenou une valeur scalaire; c'est tout pareil. Vous appelez Promise.resolvenon pas pour envelopper quelque chose dans une promesse, mais pour lancer quelque chose dans une promesse - obtenez une valeur asynchrone lorsque vous avez quelque chose ou autre.

La sémantique de asyncet awaitest tout aussi bâclée. Vous pouvez gifler awaitn'importe quelle expression non Promise dans une fonction asynchrone et tout fonctionne correctement, exactement de la même manière, sauf que vous cédez le contrôle à la file d'attente des travaux. De même, vous pouvez mettre "défensivement" async tout ce que vous voulez, tant que vous obtenez awaitle résultat. Si vous avez une fonction qui renvoie une promesse - peu importe! vous pouvez en faire une asyncfonction et, du point de vue de l'utilisateur, rien ne change (même si, techniquement, vous sortez un autre objet Promise).

Les itérateurs et générateurs asynchrones devraient fonctionner de la même manière. Tout comme vous pouvez attendre une valeur qui, accidentellement, n'était pas une promesse, un utilisateur raisonnable s'attendrait à pouvoir yield*synchroniser un itérateur dans un générateur asynchrone. for awaitles boucles devraient de la même façon «fonctionner correctement» si un utilisateur marque défensivement une boucle de cette façon, pensant qu'il pourrait peut-être obtenir un itérateur asynchrone.

Je pense que ce serait une grosse affaire de briser tous ces parallèles. Cela rendrait les itérateurs asynchrones moins ergonomiques. Discutons-en la prochaine fois que des générateurs / itérateurs asynchrones seront à l'ordre du jour du TC39.

Bergi
la source
Je vous remercie. Un événement est-il émis ou s'agit-il en fait d'un autre type d'erreur? Je demande parce que je pensais que les événements faisaient partie de la WebAPI. L'émission d'événements est-elle utilisée de façon similaire dans d'autres parties de la spécification?
Ben Aston
@ 52d6c6af Faites-vous référence aux unhandledrejectionévénements?
Bergi
Oui. C'est juste pour intercepter l '"erreur" que j'ai utilisée. window.addEventListener('unhandledrejection',...Bref: c'est la seule instance que je puisse évoquer, de ce type d'émission d'erreur par JavaScript. J'ai presque certainement tort de penser cela, cependant. Enfin: est-ce que l'émission de cette "erreur" importe vraiment au-delà d'avoir un message d'erreur indésirable dans la console?
Ben Aston
1
@ 52d6c6af Voir ici et comment cela est spécifié dans un effort conjoint entre ECMAScript et les spécifications de l'API Web. Non, l'événement n'a pas vraiment d'importance, il est déjà trop tard quand vous l'avez obtenu. Afaics, il n'est utilisé que pour surveiller les erreurs côté client.
Bergi
Si cela n'a pas vraiment d'importance, le conseil est-il toujours de "ne pas parcourir les tableaux de promesses", ou est-ce plutôt "de savoir que cela ne présente pas un comportement à toute épreuve dans certaines circonstances"?
Ben Aston
0

La sadpromesse n'étant ed quand il échoue - que les besoins de code pour terminer en attente sur avant de pouvoir commencer à attendre . La promesse échoue avant d'être résolue. ( est un outil mieux adapté à ce cas d'utilisation)awaithappysadsadhappyPromise.all

Gershom
la source
1
Je connais. D'où ma question. Si Promise.allc'est une meilleure solution, pourquoi le langage répond-il à cette syntaxe? for await...ofaurait pu facilement être implémenté pour simplement énumérer les itérables asynchrones. Mais ils l'ont pris en compte pour énumérer les itérables synchrones (mais avec un écueil (apparent?)). Pourquoi?
Ben Aston
1
Ah, j'ai mal compris. Demandons-nous pourquoi for await ... ofaccepte les itérables synchrones? J'imagine prendre en charge les générateurs asynchrones qui peuvent conditionnellement renvoyer des éléments synchrones.
Gershom
Oui, d'autant plus qu'il semble introduire un piège de rejet.
Ben Aston
À mon avis, l'écueil consiste plus généralement à créer une promesse et à ne pas l'attendre immédiatement. Malheureusement, cet écueil est également souvent une caractéristique très utile.
Gershom