【问题标题】:Why does my generator become empty after being iterated through?为什么我的生成器在迭代后会变空?
【发布时间】:2019-11-10 16:34:45
【问题描述】:

我有一个生成器通过我正在使用的库中的函数调用返回给我。然后我将这个生成器传递给一个函数,该函数遍历它并对每个项目执行一堆逻辑。然后我想在调用该函数后引用同一个生成器。但是,生成器似乎不再拥有/生成任何项目。代码大致如下:

let myGenerator = this.generatorFunc();
console.log(Array.from(myGenerator).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(myGenerator);
console.log(Array.from(myGenerator).length); //now prints 0 when I need it to be N still

iterateThroughGenerator(generator) {
    for(let element of generator) {
        // do a bunch of stuff with element
    }
}

【问题讨论】:

  • this.getGeneratorFunc()的定义是什么?
  • @PatrickRoberts 这是我正在使用的库中的定义。基本上只是用来生成一组多边形。 github.com/d3/d3-delaunay/blob/…
  • 在这种情况下,getGeneratorFunc() 的名字很糟糕。该函数生成器函数。它返回一个生成器迭代器,它只能被消费一次。
  • 因为生成器迭代器具有内部状态,可以跟踪每次调用 next() 时从其生成器函数的控制流中的哪个点恢复。一旦其内部状态到达函数的末尾,next() 将简单地返回 { value: undefined, done: true },正如 Bergi 在他的回答中所解释的那样。
  • 迭代器的目的不是它们对集合进行一次迭代,而是它们可以被增量使用,因为它们是有状态的。他们只迭代底层集合一次的事实只是有状态的限制。

标签: javascript ecmascript-6 iteration generator es6-generator


【解决方案1】:

其他人已经解释了为什么会发生这种情况,但我还是会回顾一下。

Generator functions 返回一个generator object,它使用对自身的简单循环引用实现iterable protocol(一个Symbol.iterator 属性)和iterator protocol(一个next() 方法),以便有状态地遍历其生成器函数的控制流程。

要解决您的问题,请使用实现可迭代协议的对象封装生成器函数调用,方法是返回生成器对象的单独实例,您可以以相同的方式使用它:

const iterable = { [Symbol.iterator]: () => this.generatorFunc() };

console.log(Array.from(iterable).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(iterable);
console.log(Array.from(iterable).length); //still prints N

iterateThroughGenerator(iterable) {
    for(let element of iterable) {
        // do a bunch of stuff with element
    }
}

【讨论】:

    【解决方案2】:

    这就是迭代器的工作方式。调用生成器会返回一个迭代器,它可以迭代一次。在大多数其他语言中也是如此。

    let generator = function* () {
      for (let i = 0; i < 3; i++) 
        yield i;
    };
    
    let iterator = generator();
    
    console.log(Array.from(iterator)); // [1...3]
    console.log(Array.from(iterator)); // []
    
    console.log(Array.from(generator())); // [1..3]
    console.log(Array.from(generator())); // [1..3]

    【讨论】:

    • 我明白了。为什么迭代器只能迭代一次?在某些情况下,您需要一个可以多次使用的迭代器。迭代器是否有某种属性允许这种情况发生?
    • 我的直觉是简单;大多数迭代器实现包括bool hasNext()T next() 方法,但没有void restart() 方法。如果你还没有,作为一个有趣的练习题,尝试实现一个迭代器。另请参阅stackoverflow.com/questions/7689261/…
    • @TomLisankie 迭代器是一个有状态的对象,你迭代它一次直到它结束,然后你把它扔掉。如果你想再次迭代你的可迭代对象(数组,不管,......),你创建一个新的迭代器。 (当然可以编写一个迭代器,在它结束时重置,但这不是它们通常的工作方式和消耗方式)。
    【解决方案3】:

    Array.from() 耗尽了生成器(迭代它直到结束),并且进一步迭代它不会产生更多元素。 next() 调用将始终返回 {value: undefined, done: true}

    要创建一个从头开始的新生成器,您需要再次调用generatorFunc()

    【讨论】:

      【解决方案4】:

      一旦生成器函数完成,你必须调用 this.getGeneratorFunc() 来重新创建生成器。此外,当您执行 Array.from(myGenerator) 时,它也会完成该生成器,因此当您调用 this.iterateThroughGenerator(myGenerator) 时,将不会发生任何事情,因为不再从生成器返回任何元素。因此,您可以将生成器的结果保存到数组中并重用该数组,或者每次要从中获取元素时调用 this.getGeneratorFunc() 三次。在这个具体的例子中,我会这样做

       const generated = Array.from(this.getGeneratorFunc());
       console.log(generated.length);
       this.iteratedItems(generated);
       console.log(generated.length);
      

      也请查看此答案。 Previous Answer
      我也读过this

      【讨论】:

        猜你喜欢
        • 2020-06-29
        • 2013-03-08
        • 1970-01-01
        • 2021-03-21
        • 2017-09-15
        • 2019-05-12
        • 1970-01-01
        • 2018-08-16
        • 1970-01-01
        相关资源
        最近更新 更多