简体   繁体   English

在多个函数/方法中重用生成器

[英]Reusing a Generator in Multiple Functions/Methods

I am trying to create a file reader object (from readFileSync) and serve the lines from a generator function.我正在尝试创建一个文件阅读器 object(来自 readFileSync)并提供来自生成器 function 的行。 My intention is to pass this gnerator object to multiple functions and sequentialy parse a file.我的意图是将这个生成器 object 传递给多个函数并顺序解析文件。 However, after using the generator in a single function, the state of the generator shift from suspended to closed.但是,在单个 function 中使用发电机后,发电机的 state 由暂停变为关闭。 I come from a Python background and this is a very much possible operation in Python.我来自 Python 背景,这是 Python 中非常可能的操作。 Would like to know what I am doing wrong here.想知道我在这里做错了什么。 Following is the code I used:以下是我使用的代码:

Generator function definition (I am using readFileSync and it is not async, please disregard that for the time being as I am trying to get the generator working):生成器 function 定义(我正在使用 readFileSync 并且它不是异步的,请暂时忽略它,因为我正在尝试使生成器工作):

function* getFileGen(path: string){
  const fileContent = fs
  .readFileSync(path, {
    encoding: "utf-8",
    flag: "r",
  })
  .split("\n");

  while(true){
      const thisLine = fileContent.shift();
      if(!thisLine){
        break;
      }
      yield thisLine; 
  }
}

The two functions in which I would like to use the generator in:我想在其中使用生成器的两个函数:

function getFirstFew(stream: Generator){
  let i = 0;
  for(let v of stream){
    console.log(v);
    if(i > 1){
      break;
    }
    i++;
  }
}

function getNextFew(stream: Generator){
  let i = 0;
  for(let v of stream){
    console.log(v);
    if(i > 7){
      break;
    }
    i++;
  }

And finally create a generator and pass it sequentially to two functions that would print a number of lines:最后创建一个生成器并将其依次传递给两个将打印多行的函数:

const myStream = getFileGen('path/to/file');

getFirstFew(myStream);
getNextFew(myStream);

The first function executes correctly and prints 3 lines;第一个 function 执行正确,打印 3 行; however by the time the generator is passed to the getNextFew function, it has already closed.但是,当生成器传递给 getNextFew function 时,它已经关闭了。

From the docs :文档

In for...of loops, abrupt iteration termination can be caused by break, throw or return.在 for...of 循环中,突然的迭代终止可能由 break、throw 或 return 引起。 In these cases, the iterator is closed .在这些情况下,迭代器是关闭的

And, specifically:并且,具体来说:

Do not reuse generators不要重复使用生成器

Generators should not be re-used, even if the for...of loop is terminated early, for example via the break keyword .生成器不应该被重复使用,即使 for...of 循环提前终止,例如通过 break 关键字 Upon exiting a loop, the generator is closed and trying to iterate over it again does not yield any further results.退出循环后,生成器将关闭,尝试再次对其进行迭代不会产生任何进一步的结果。

Emphasis mine.强调我的。

I'll admit though, I'm not very strong with JS, so I can't recommend a comparable workaround.不过我承认,我对 JS 不是很擅长,所以我不能推荐类似的解决方法。 You may need to use a list or another strict structure that you have more control over.您可能需要使用您可以更好地控制的列表或其他严格的结构。

You may be able to implement a tee function comparable to Python's tee that creates a copy of the iterator, then iterate one of the copies.您可能能够实现与 Python 的tee相当的tee function,它创建迭代器的副本,然后迭代其中一个副本。

This is a great application for Node's streams API.这是 Node 的流 API 的一个很好的应用程序。

You can use a generator as a source for a Node.js ReadableStream , then duplicate it using PassThrough .您可以使用生成器作为 Node.js ReadableStream 的源,然后使用 PassThrough 复制它

A bit like this answer here: Node.js Piping the same readable stream into multiple (writable) targets有点像这里的答案: Node.js 将相同的可读 stream 输送到多个(可写)目标中

Basically you can do this:基本上你可以这样做:

const { PassThrough, Readable } = require("stream");

function* getFileGen(path: string) {
  const fileContent = fs
  .readFileSync(path, {
    encoding: "utf-8",
    flag: "r",
  })
  .split("\n");

  while(true) {
      const thisLine = fileContent.shift();
      if(!thisLine) {
        break;
      }
      yield thisLine; 
  }
}

// Set up per: https://nodejs.org/api/stream.html#streamreadablefromiterable-options
const fileReadlineStream = Readable.from(getFileGen(), { objectMode: false });

const firstFew = new PassThrough();
const nextFew = new PassThrough();

fileReadlineStream.pipe(firstFew);
fileReadlineStream.pipe(nextFew);

firstFew.on("data", (d) => {
    console.log("First few received:", d);
});

nextFew.on("data", (d) => {
    console.log("Next few received:", d);
});

If you want to transform that file in some way you will be best off using a TransformStream.如果您想以某种方式转换该文件,最好使用 TransformStream。 In execution you might want to prioritise one stream's processing over another;在执行过程中,您可能希望将一个流的处理优先于另一个; I'm not an expert on the exact behaviour but generally you can handle that with buffering .我不是确切行为的专家,但通常您可以使用 buffering 来处理

A for of loop always closes the iterator by calling .return() on it in the end (if the method exists). for of循环总是通过在最后调用.return()来关闭迭代器(如果该方法存在)。 You can prevent that by removing (overwriting) the method您可以通过删除(覆盖)该方法来防止这种情况

const myStream = getFileGen('path/to/file');
myStream.return = undefined;
getFirstFew(myStream);
getNextFew(myStream);

but that's crude.但这很粗糙。 I'd rather do something like我宁愿做类似的事情

function keptOpen(iterable) {
    const iterator = iterable[Symbol.iterator]()
    return {
        [Symbol.iterator]() { return this; },
        next(v) { return iterator.next(); },
    };
}

and use it like并像使用它一样

const myStream = keptOpen(getFileGen('path/to/file'));
getFirstFew(myStream);
getNextFew(myStream);

However, notice that this will work only with generator functions that don't care about being kept open.但是,请注意,这仅适用于不关心保持打开状态的生成器函数。 If they expect that their .return() method is called so that they can dispose their allocated resources, keptOpen will make them leak.如果他们期望调用他们的.return()方法以便他们可以处理分配的资源, keptOpen将使它们泄漏。

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

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