用于同步迭代器的等待 [英] Using for await...of with synchronous iterables
问题描述
MDN 说 for await...of
有两个用例:
MDN says for await...of
has two use-cases:
for await...of
语句创建一个遍历异步的循环 可迭代对象以及同步可迭代对象,...
The
for await...of
statement creates a loop iterating over async iterable objects as well as on sync iterables,...
我以前知道前者:使用Symbol.asyncIterator
异步可迭代.但是我现在对后者感兴趣:同步可迭代对象.
I was previously aware of the former: async iterables using Symbol.asyncIterator
. But I am now interested in the latter: synchronous iterables.
以下代码在一个同步可迭代对象(一个promise数组)上进行迭代.似乎阻碍了每项诺言的实现.
The following code iterates over a synchronous iterable - an array of promises. It appears to block progess on the fulfilment of each 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)
按照以下所示的逻辑,该行为似乎类似于依次等待每个诺言.这个主张正确吗?
The behavior appears to be akin to awaiting each promise in-turn, per the logic shown below. Is this assertion correct?
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(let p of promises) {
const item = await p
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.all
和Promise.allSettled
可以避免的隐式拒绝连接陷阱,对于我来说,这种模式将被语言明确支持是很奇怪的.
I ask because this pattern of code has an implicit rejection wire-up pitfall that Promise.all
and Promise.allSettled
avoid, and it seems strange to me that this pattern would be explicitly supported by the language.
window.addEventListener('unhandledrejection', () => {
console.log('unhandled rejection; `sad` was not being awaited at the time it rejected')
})
async function asyncFunction() {
try {
const happy = new Promise((resolve)=>setTimeout(()=>resolve('success'), 1000))
const sad = new Promise((_,reject)=>setTimeout(()=>reject('failure')))
const promises = [happy, sad]
for(let p of promises) {
const item = await p
console.log(item)
}
} catch (err) {
console.log(`an error occurred:`, err)
}
}
asyncFunction() // "unhandled rejection; `sad` was not being awaited at the time it rejected" (after about zero seconds), and then "happy, an error occurred: sad" (printed in quick succession, after about 5 seconds)
推荐答案
是的,很奇怪,您不应该这样做.不要迭代承诺的数组,它会导致完全导致您提到的未处理拒绝问题. (另请参见此更具体的说明.)
Yes, it is strange, and you should not do this. Don't iterate arrays of promises, it leads exactly to the unhandled-rejections problem you mentioned. (See also this more specific explanation.)
那么为什么在语言中支持此功能?继续草率的诺言语义.
So why is this supported in the language? To continue with the sloppy promise semantics.
我认为我们应该退回到
Symbol.iterator
,因为我们当前 承诺语义是关于允许将同步内容用作 异步的东西.您可能将其称为草率".它遵循 @groundwater上面的逻辑, 但我只想更详细地说明相似之处.
I think we should fall back to
Symbol.iterator
because our current Promise semantics are all about allowing sync things to be used as async things. You might call this "sloppiness". It follows @groundwater's logic above, but I just want to spell out the parallels in more detail.
.then
的链接"语义与此有关.您可以返回一个
从.then
或标量值保证;全部都是一样.你打电话
Promise.resolve
不是将某些内容包装在Promise中,而是进行投射
承诺-拥有时获取异步值
某事或其他.
The "chaining" semantics of .then
are all about this. You can return a
Promise from .then
or a scalar value; it's all the same. You call
Promise.resolve
not to wrap something in a Promise, but to cast
something to a Promise--get an asynchronous value when you have
something-or-other.
async
和await
的语义也都是草率的.
您可以在异步函数中的任何非Promise表达式上拍击await
一切工作正常,完全相同,只是屈服
控制作业队列.同样,您可以防御性地"放置async
只要您await
结果,就可以随心所欲.如果你有
一个返回Promise的函数-无论如何!你可以做一个
async
功能,并且从用户角度看,没有任何变化(甚至
如果从技术上讲,您得到了另一个Promise对象).
The semantics of async
and await
are all about being sloppy as well.
You can slap await
on any non-Promise expression in an async function
and everything works fine, exactly the same way, except that you yield
control to the job queue. Similarly, you can "defensively" put async
around whatever you want, as long as you await
the result. If you have
a function that returns a Promise--whatever! you can make that an
async
function, and, from a user perspective, nothing changes (even
if, technically, you get a different Promise object out).
异步迭代器和生成器应该以相同的方式工作.就像你一样
可以等待偶然地不是一个承诺,合理的价值
用户期望能够在异步中yield*
同步迭代器
发电机.如果用户,for await
循环应类似地正常工作"
以这种方式在防守端标记了一个循环,认为它们可能是
得到一个异步迭代器.
Async iterators and generators should work the same way. Just like you
can await a value that, accidentally, wasn't a Promise, a reasonable
user would expect to be able to yield*
a sync iterator within an async
generator. for await
loops should similarly "just work" if a user
defensively marks a loop that way, thinking that they maybe might be
getting an async iterator.
我认为打破所有这些相似之处非常重要.它 会使异步迭代器的工效性降低.让我们接下来讨论这个 时间异步生成器/迭代器出现在TC39的议程上.
I think it would be a big deal to break all of these parallels. It would make async iterators less ergonomic. Let's discuss this the next time async generators/iterators come up on the agenda at TC39.
这篇关于用于同步迭代器的等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!