简体   繁体   English

异步生成器 class 卡在无限循环 javascript

[英]Async generator class stuck on infinite loop javascript

I'm trying to get the following async generator to work:我正在尝试使以下异步生成器工作:

class MyIterator {
  constructor(m) {
    this.collection = m;
  }

   async *[Symbol.iterator]() {
      for (let item of this.collection) {
        const resultItem = await Promise.resolve(item)
        console.log("item: ", resultItem)
        yield resultItem
      }
  }
}
(async () => {
  const iterator = new MyIterator([1,2,3])
  let times = 0
  for await (let thing of iterator) {
    console.log("thing: ", thing)

    // this is here to avoid an infinite loop
    times++
    if (times > 1000) break
  }
})()

But it ends up in an infinite loop, and thing is always undefined.但它最终会陷入无限循环,并且thing总是未定义的。

item: 1
thing: undefined
item: 2
thing: undefined
item: 3
thing: undefined (x999)

I've tried a similar code, but this time without the Promise/async behaviour, and it seems to work just fine.我尝试过类似的代码,但这次没有Promise/async行为,它似乎工作得很好。

class MyIterator {
  constructor(m) {
    this.collection = m;
  }

   *[Symbol.iterator]() {
      for (let item of this.collection) {
        console.log("item: ", item)
        yield item
      }
  }
}

const iterator = new MyIterator([1,2,3])
for (let thing of iterator) {
  console.log("thing: ", thing)
}
item: 1
thing: 1
item: 2
thing: 2
item: 3
thing: 3

The for await..of construct will attempt to iterate over an async iterator. for await..of构造将尝试迭代异步迭代器。

An async iterator is defined using the @@asyncIterator well-known symbol :使用@@asyncIterator众所周知的符号定义异步迭代器:

 class MyIterator { constructor(m) { this.collection = m; } async *[Symbol.asyncIterator]() { //<-- this is async for (let item of this.collection) { const resultItem = await Promise.resolve(item) //console.log("item: ", resultItem) yield resultItem } } } (async () => { const iterator = new MyIterator([1,2,3]) let times = 0 for await (let thing of iterator) { //no infinite loop console.log("thing: ", thing) } })()

for await..of can also consume plain iterables that produce promises: for await..of也可以使用产生承诺的普通迭代:

 const promiseArray = [Promise.resolve("a"), Promise.resolve("b"), Promise.resolve("c")]; (async function() { for await(const item of promiseArray) { console.log(item); } })()

Attempting to make a regular iterator that is an async method/function does not work.尝试创建作为异步方法/函数的常规迭代器不起作用。

If you want to keep your @@iterator defined method your the best choice is to make it produce promises instead:如果你想保留@@iterator定义的方法,最好的选择是让它产生 Promise:

 class MyIterator { constructor(m) { this.collection = m; } *[Symbol.iterator]() { // not async for (let item of this.collection) { yield Promise.resolve(item); //produce a promise } } } (async () => { const iterator = new MyIterator([1,2,3]) let times = 0 for await (let thing of iterator) { console.log("thing: ", thing) } })()

Although, that's might be a bad practice if any of the promises rejects:虽然,如果任何承诺拒绝,这可能是一个不好的做法

 const wait = (ms, val) => new Promise(res => setTimeout(res, ms, val)); const fail = (ms, val) => new Promise((_, rej) => setTimeout(rej, ms, val)); const arr = [ wait(100, 1), wait(150, 2), fail(0, "boom"), wait(200, 3) ]; (async function(){ try { for await (const item of arr) { console.log(item); } } catch (e) { console.error(e); } })() /* result in the browser console: Uncaught (in promise) boom 1 2 boom */

运行上述代码段的浏览器控制台屏幕截图。结果与片段末尾的注释相同。

However, be aware that there is a difference in semantics between these:但是,请注意,这些之间存在语义差异:

  • A regular iterator produces an IteratorResult - an object with value and done properties.常规迭代器产生一个 IteratorResult - 一个具有valuedone属性的 object。

 const syncIterable = { [Symbol.iterator]() { return { next() { return {value: 1, done: true} } } } } const syncIterator = syncIterable[Symbol.iterator](); console.log("sync IteratorResult", syncIterator.next());

  • An async generator produces a promise for an IteratorResult异步生成器为 IteratorResult 生成 promise

 const asyncIterable = { [Symbol.asyncIterator]() { return { next() { return Promise.resolve({value: 2, done: true}); } } } } const asyncIterator = asyncIterable[Symbol.asyncIterator](); asyncIterator.next().then(result => console.log("async IteratorResult", result));

  • Finally, an iterator that produces promises will have an IteratorResult where value is a promise:最后,产生 promise 的迭代器将有一个 IteratorResult,其value promise:

 const promiseSyncIterable = { [Symbol.iterator]() { return { next() { return {value: Promise.resolve(3), done: true} } } } } const promiseSyncIterator = promiseSyncIterable[Symbol.iterator](); const syncPromiseIteratorResult = promiseSyncIterator.next(); console.log("sync IteratorResult with promise", syncPromiseIteratorResult); syncPromiseIteratorResult.value.then(value => console.log("value of sync IteratorResult with promise", value));


Side-note on nomenclature: MyIterator is not an iterator.关于命名的旁注: MyIterator不是迭代器。 An iterator is an object with a next() method which produces an IteratorResult.一个迭代器是一个 object 和一个next()方法,它产生一个 IteratorResult。 An object that you can iterate over has an @@iterator (or @@asyncIterable ) method and it is called iterable (or async iterable respectively).您可以迭代的 object 有一个@@iterator (或@@asyncIterable )方法,它被称为可迭代(或分别称为异步迭代)。

As @VLAZ pointed out in a comment to my question, I was using Symbol.iterator instead of Symbol.asyncIterator .正如@VLAZ在对我的问题的评论中指出的那样,我使用的是Symbol.iterator而不是Symbol.asyncIterator The following implementation works as expected:以下实现按预期工作:

class MyIterator {
  constructor(m) {
    this.collection = m;
  }

   async *[Symbol.asyncIterator]() {
      for (let item of this.collection) {
        const resultItem = await Promise.resolve(item)
        console.log("item: ", resultItem)
        yield resultItem
      }
  }
}
(async () => {
  const iterator = new MyIterator([1,2,3])
  for await (let thing of iterator) {
    console.log("thing: ", thing)
  }
})()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM