【问题标题】:Turning a callback which gets called multiple times into a generator function将多次调用的回调转换为生成器函数
【发布时间】:2019-03-10 21:35:09
【问题描述】:

我有一个异步获取数据库行的函数,并为每一行调用一个回调函数。我正在尝试编写一个包装器,它是一个生成器函数,每次返回一行时都会产生,但我没有看到如何正确产生。

原始代码如下所示:

db.each(query, (err, row) => {
  // do something here with row
}, () => {
  // called after the last row is returned
})

我熟悉生成器的工作原理,但产量似乎属于生成器函数本身,而不是匿名函数。所以我认为这样的事情是行不通的:

function* dbEach(db, query) {
    db.each(query, (err, row) => {
      yield row
    })
}

当我真正尝试时,我得到一个错误“意外的标识符”。

我进一步看了一下,似乎 ES2018 现在有异步迭代器,它们应该使这成为可能。但是,在我已经有一个被多次调用的回调的情况下,我很难弄清楚如何准确地使用异步迭代器。

【问题讨论】:

  • yield来自一个内部的非生成器函数。
  • 不确定这是否是您需要的,function* dbEach(db, query) { yield db.each(query, (err, row) => row); }
  • @Rikin 哦...也许yield*?
  • 如何知道所有.each 回调何时完成?是否有 .finished 属性或类似的东西可以检查? (也许有比 each 更好的函数,您可以从中获取所有行作为 array,这会使事情变得容易得多)
  • @CertainPerformance 还有另一个可选回调。

标签: javascript callback generator


【解决方案1】:

您可以使生成器async,然后将await 设为可解析所有行的Promise(这样您就可以引用dbEach 顶层的rows 变量),然后您就可以yield rows 数组中的每一行:

async function* dbEach(db, query) {
  const rows = await new Promise((resolve, reject) => {
    const rows = [];
    db.each(query, (err, row) => {
      if (err) reject(err);
      else rows.push(row);
    }, () => resolve(rows));
  });
  for (const row of rows) {
    yield row;
  }
}

用于:

for await (const row of dbEach(...)) {
  // do something
}

看起来.each 是为在每一行上运行回调而设计的,这对于您在这里尝试使用生成器完成的任务而言并不是最佳选择 - 如果可能的话,如果有方法就好了在您的数据库中,它允许您获取 array 行,例如:

async function* dbEach(db, query) {
  const rows = await new Promise((resolve, reject) => {
    db.getAllRows(query, (err, rows) => {
      if (err) reject(err);
      else resolve(rows);
    });
  });
  for (const row of rows) {
    yield row;
  }
}

不过,我不认为生成器在这里有很大帮助 - 你也可以只 await 一个 Promise 来解析行,并同步迭代行:

function getRows(db, query) {
  return new Promise((resolve, reject) => {
    db.getAllRows(query, (err, rows) => {
      if (err) reject(err);
      else resolve(rows);
    });
  });
}

const rows = await getRows(...);
for (const row of rows) {
  // ...
}

【讨论】:

  • 获取行数组的问题是(例如)内存中缓冲了 5000 万行...
  • 鉴于回调是由 db 的 internals 调用的,并且所述调用不能从外部控制/节流,我有点怀疑生成器是否会能够与那个交互,而没有有很多行可能“堆积”。例如,如果在生成器消费者有空闲资源进行操作之前调用了 1000 次回调会怎样?
  • await 不进入async 的主体如何工作?
  • @zer00ne OP 正在寻求使用新的异步迭代语法,这当然要求其 await 位于 async 函数中,但 OP 遇到的问题是弄清楚如何设置生成器,而不是如何使用await。当然,包含块必须是 async 函数
猜你喜欢
  • 2019-10-17
  • 2018-07-09
  • 2015-06-24
  • 2016-05-20
  • 1970-01-01
  • 2020-02-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多