使用 for await...of 同步迭代 [英] 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.
以下代码迭代一个同步可迭代对象 - 一个承诺数组.它似乎阻碍了每个承诺的实现.
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.)
那么为什么该语言支持此功能?继续草率的 promise 语义.
So why is this supported in the language? To continue with the sloppy promise semantics.
我认为我们应该回到 Symbol.iterator
因为我们当前的Promise 语义都是关于允许将同步的东西用作异步的东西.你可以称之为草率".它跟随@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
的链接"语义就是关于这个的.你可以返回一个Promise 来自 .then
或一个标量值;全部都是一样.你打电话Promise.resolve
不是将某些东西包装在 Promise 中,而是进行强制转换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).
异步迭代器和生成器应该以相同的方式工作.就像你一样可以等待一个意外地不是 Promise 的值,一个合理的用户希望能够在异步中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.
这篇关于使用 for await...of 同步迭代的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!