用于同步迭代器的等待


11

MDN for await...of有两个用例:

for await...of语句创建一个循环,循环遍历异步可迭代对象以及同步可迭代对象,...

我以前知道前者:使用异步可迭代Symbol.asyncIterator。但是我现在对后者感兴趣:同步可迭代。

以下代码在一个同步可迭代对象(一个promise数组)上进行迭代。它似乎阻碍了每个诺言的实现。

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)

根据以下所示的逻辑,行为似乎类似于等待每个承诺。这个主张正确吗?

我问,因为这个代码模式有一个隐含的排斥线了陷阱是Promise.allPromise.allSettled避免,这似乎很奇怪,我认为这种模式会被语言的明确支持。


2
你到底是什么问题?好像你提供工作的例子
史记丽卡

我对for await... of同步可迭代对象的描述是否正确,如果可以,那么该模式会产生未处理的拒绝错误是否重要?
本阿斯顿

“它是正确的”不是问题。您所说的就是“正确”。
罗伯特·哈维

您可以通过代码演示所描述的未处理拒绝错误的产生吗?
罗伯特·哈维

最终代码演示了这一点。在这种情况下,正确具有明确定义的含义,因为我提供了描述我认为正在做的事情的代码。如果行为与我的代码匹配,则我的代码正确,否则我的理解不正确。您也说的是“正确”的观察结果显然是不正确的。在此上下文中,正确具有明确定义的含义。
本阿斯顿

Answers:


4

是的,这很奇怪,您不应该这样做。不要迭代promise的数组,它会导致您提到的unhandled-rejection问题

那么,为什么在语言中支持此功能?继续草率的承诺语义。

您可以在讨论提案的这一部分的问题的评论中找到确切的理由:

我认为我们应该退一步,Symbol.iterator因为我们当前的Promise语义都是关于允许将同步事物用作异步事物。您可能称其为“草率”。它遵循 上面@groundwater的逻辑,但是我只想更详细地阐明相似之处。

的“链接”语义.then都与此有关。您可以返回Promise from .then或标量值;这都一样。您打电话来 Promise.resolve不是将某些内容包装在Promise中,而是将某些内容转换为Promise-当您有其他东西时获取异步值。

和的语义asyncawait都是草率的。您可以await对异步函数中的任何非Promise表达式进行拍打,并且除了将控制权交给工作队列之外,其他方法都可以正常工作,完全一样。同样,async 只要您await得到结果,您就可以“防御”放置您想要的任何东西。如果您有一个返回Promise的函数-无论如何!您可以使它成为一个 async函数,并且从用户的角度看,没有任何改变(即使从技术上讲,您得到了另一个Promise对象)。

异步迭代器和生成器应以相同的方式工作。就像您可以等待一个偶然地不是Promise的值一样,合理的用户也希望能够yield*在异步生成器中进行同步迭代器。for await如果用户以这种方式防御性地标记一个循环,并认为它们可能正在获取异步迭代器,则循环应类似地“正常工作”。

我认为打破所有这些相似之处非常重要。这将使异步迭代器的工效性降低。下次在TC39上,异步生成器/迭代器出现在议程上时,让我们讨论一下。


谢谢。是发出事件,还是实际上是其他某种错误?我问是因为我认为事件是WebAPI的一部分。在规范的其他部分中,事件的发出是否以类似的方式使用?
本·阿斯顿

@ 52d6c6af您是在指unhandledrejection事件吗?
Bergi

是。window.addEventListener('unhandledrejection',...简而言之,就是拦截我使用的“错误” :这是我能想到的唯一一个实例,这种实例是JavaScript产生的错误。但是,我几乎可以肯定认为这是错误的。最后:发出“错误”消息是否真的比在控制台中出现不需要的错误消息重要吗?
本阿斯顿

1
@ 52d6c6af有关如何在ECMAScript和Web API规范之间共同制定的说明,请参见此处那里。不,事件并不重要,收到此消息已经为时已晚。Afaics,它仅用于监视客户端错误。
贝尔吉

如果这并不重要,建议还是“不要迭代承诺”,还是“请注意在某些情况下这不会表现出快速失败的行为”?
本阿斯顿

0

sad承诺没有await编在失败时-代码需要完成等待happy,才可以开始等待sad。该sad承诺是之前没有happy消退。(Promise.all是一种更适合此用例的工具)


1
我知道。因此,我的问题。如果Promise.all是更好的解决方案,为什么该语言可以满足这种语法?for await...of可以很容易地实现以简单地枚举异步可迭代对象。但是他们迎合了它枚举同步可迭代对象的问题(但有一个陷阱)。为什么?
本阿斯顿

1
啊,我误会了。我们是否在问为什么for await ... of接受同步迭代?我想过要支持异步生成器,该生成器有条件地可能返回同步项。
Gershom

是的,尤其是考虑到似乎引入了拒收陷阱。
本阿斯顿

我认为,陷阱更普遍的是创造承诺,而不是立即等待。不幸的是,这种陷阱通常也是非常有用的功能。
Gershom
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.