在多个 Functions/Methods 中重复使用生成器
Reusing a Generator in Multiple Functions/Methods
我正在尝试创建文件 reader 对象(来自 readFileSync)并提供来自生成器函数的行。我的目的是将这个生成器对象传递给多个函数并顺序解析一个文件。但是,在单个函数中使用生成器后,生成器的状态从挂起变为关闭。我来自 Python 背景,这在 Python 中是一个非常可能的操作。想知道我在这里做错了什么。以下是我使用的代码:
生成器函数定义(我正在使用 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;
}
}
我想在其中使用生成器的两个函数:
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++;
}
最后创建一个生成器并将其按顺序传递给两个将打印多行的函数:
const myStream = getFileGen('path/to/file');
getFirstFew(myStream);
getNextFew(myStream);
第一个函数正确执行并打印了3行;但是当生成器传递给 getNextFew 函数时,它已经关闭了。
来自docs:
In for...of loops, abrupt iteration termination can be caused by break, throw or return. In these cases, the iterator is closed.
并且,具体来说:
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. Upon exiting a loop, the generator is closed and trying to iterate over it again does not yield any further results.
强调我的。
虽然我承认,我对 JS 不是很了解,所以我不能推荐类似的解决方法。您可能需要使用列表或其他您可以更好控制的严格结构。
您可以实现一个 tee
函数,类似于 Python 的 tee
创建迭代器的副本,然后迭代其中一个副本。
这是一个很好的 Node 流应用程序 API。
你可以use a generator as a source for a Node.js ReadableStream, then duplicate it using PassThrough.
有点像这里的回答:Node.js Piping the same readable stream into multiple (writable) targets
基本上你可以这样做:
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);
});
如果您想以某种方式转换该文件,最好使用 TransformStream。在执行过程中,您可能希望将一个流的处理优先于另一个;我不是确切行为的专家,但通常你可以 handle that with buffering.
A for of
循环总是通过最后调用 .return()
来关闭迭代器(如果该方法存在)。您可以通过删除(覆盖)方法来防止这种情况
const myStream = getFileGen('path/to/file');
myStream.return = undefined;
getFirstFew(myStream);
getNextFew(myStream);
但这很粗糙。我宁愿做类似
的事情
function keptOpen(iterable) {
const iterator = iterable[Symbol.iterator]()
return {
[Symbol.iterator]() { return this; },
next(v) { return iterator.next(); },
};
}
并像
一样使用它
const myStream = keptOpen(getFileGen('path/to/file'));
getFirstFew(myStream);
getNextFew(myStream);
但是,请注意,这仅适用于不关心是否保持打开状态的生成器函数。如果他们期望他们的 .return()
方法被调用以便他们可以处理他们分配的资源,keptOpen
将使他们泄漏。
我正在尝试创建文件 reader 对象(来自 readFileSync)并提供来自生成器函数的行。我的目的是将这个生成器对象传递给多个函数并顺序解析一个文件。但是,在单个函数中使用生成器后,生成器的状态从挂起变为关闭。我来自 Python 背景,这在 Python 中是一个非常可能的操作。想知道我在这里做错了什么。以下是我使用的代码:
生成器函数定义(我正在使用 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;
}
}
我想在其中使用生成器的两个函数:
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++;
}
最后创建一个生成器并将其按顺序传递给两个将打印多行的函数:
const myStream = getFileGen('path/to/file');
getFirstFew(myStream);
getNextFew(myStream);
第一个函数正确执行并打印了3行;但是当生成器传递给 getNextFew 函数时,它已经关闭了。
来自docs:
In for...of loops, abrupt iteration termination can be caused by break, throw or return. In these cases, the iterator is closed.
并且,具体来说:
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. Upon exiting a loop, the generator is closed and trying to iterate over it again does not yield any further results.
强调我的。
虽然我承认,我对 JS 不是很了解,所以我不能推荐类似的解决方法。您可能需要使用列表或其他您可以更好控制的严格结构。
您可以实现一个 tee
函数,类似于 Python 的 tee
创建迭代器的副本,然后迭代其中一个副本。
这是一个很好的 Node 流应用程序 API。
你可以use a generator as a source for a Node.js ReadableStream, then duplicate it using PassThrough.
有点像这里的回答:Node.js Piping the same readable stream into multiple (writable) targets
基本上你可以这样做:
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);
});
如果您想以某种方式转换该文件,最好使用 TransformStream。在执行过程中,您可能希望将一个流的处理优先于另一个;我不是确切行为的专家,但通常你可以 handle that with buffering.
A for of
循环总是通过最后调用 .return()
来关闭迭代器(如果该方法存在)。您可以通过删除(覆盖)方法来防止这种情况
const myStream = getFileGen('path/to/file');
myStream.return = undefined;
getFirstFew(myStream);
getNextFew(myStream);
但这很粗糙。我宁愿做类似
的事情function keptOpen(iterable) {
const iterator = iterable[Symbol.iterator]()
return {
[Symbol.iterator]() { return this; },
next(v) { return iterator.next(); },
};
}
并像
一样使用它const myStream = keptOpen(getFileGen('path/to/file'));
getFirstFew(myStream);
getNextFew(myStream);
但是,请注意,这仅适用于不关心是否保持打开状态的生成器函数。如果他们期望他们的 .return()
方法被调用以便他们可以处理他们分配的资源,keptOpen
将使他们泄漏。