【发布时间】:2019-01-29 14:27:05
【问题描述】:
我正在努力将基于回调的旧 API 转换为异步库。但我就是无法将“结果集”作为生成器(节点 10.x)工作。
原来的 API 是这样工作的:
api.prepare((err, rs) => {
rs.fetchRows(
(err, row) => {
// this callback is called as many times as rows exist
console.log("here's a row:", row);
},
() => {
console.log("we're done, data exausted");
}
);
});
但这是我想使用它的方式:
const wrapped = new ApiWrapper(api);
const rs = await wrapped.prepare({});
for (let row of rs.rows()) {
console.log("here's a row:", row);
}
let row;
while(row = await rs.next()) {
console.log("here's a row:", row);
}
我以为我可以使用生成器来控制它,但看起来你不能在回调中使用 yield。如果你仔细想想,这实际上似乎是合乎逻辑的。
class ApiWrapper {
constructor(api) {
this.api = api;
}
prepare() {
return new Promise((resolve, reject) => {
this.api.prepare((err, rs) => {
if (err) {
reject(err);
} else {
resolve(rs);
}
});
});
}
*rows() {
this.api.fetchRows((err, row) => {
if (err) {
throw err;
} else {
yield row; // nope, not allowed here
}
});
}
next() { ... }
}
那么我有什么选择呢?
重要提示:我不想在数组中存储任何内容然后对其进行迭代,我们在这里讨论的是千兆负载的行数据。
编辑
我可以使用stream.Readable 模拟我想要的行为,但它警告我这是一个实验性功能。这是我尝试使用stream 解决的基于数组的简化版本:
const stream = require('stream');
function gen(){
const s = new stream.Readable({
objectMode: true,
read(){
[11, 22, 33].forEach(row => {
this.push({ value: row });
});
this.push(null)
}
});
return s;
}
for await (let row of gen()) {
console.log(row);
}
// { value: 11 }
// { value: 22 }
// { value: 33 }
(node:97157) ExperimentalWarning: Readable[Symbol.asyncIterator] is an experimental feature. This feature could change at any time
【问题讨论】:
-
生成器不是异步的。您在寻找AsyncIterator 吗?
-
不确定,但它可以工作。我去看看,谢谢。
-
AsyncIterator 可能是要走的路,但我仍然不明白如何从回调中返回迭代器或生成器。我需要像可迭代的
Promise这样的东西,它可以为到达回调的每一行数据实现。我可以接近这一点是通过返回一个流(它们是可迭代的),每个回调都会将行推入。 -
迭代器的每个
next调用都需要为下一行创建一个promise - 您可能需要为此创建一个解析器队列。 -
我想我已经找到了答案,@bergi 关于“队列”的说法我尝试了一个不起作用的队列实现,然后意识到我需要像 Go 的通道这样可以与 async/await 一起使用的东西.这个精彩的模块做到了:npmjs.com/package/@nodeguy/channel。我会尽快发布答案。
标签: javascript node.js ecmascript-6