等到数据库查询完成

Wait until DB query is done

我很确定我的问题与 Koa 或 SQLite3 的使用没有直接关系,而是与 JavaScript.

的一般使用有关

我有以下 server.js:

var http = require('http');
var koa = require('koa');
var route = require('koa-route');
var app = koa();
var sqlite3 = require('sqlite3').verbose();
var dbFile = 'coc.db';
var db = new sqlite3.Database(dbFile);

var members = [];

var printMembers = function(name){

        db.each(query, [name], function(err, row){
            members.push(row);
        });

    return members;

}


app.use(route.get('/', index));


function *index() {

    this.body = printMembers("name");

}


app.listen(8008);
console.log('Koa listening on port 8008');

当我启动服务器并第一次访问 localhost:8008 时,我只得到一个空数组 ([]) 作为响应。这是变量members的内容。当我重新加载时,我得到了预期的 SQLite 查询结果。

原因可能是因为我的查询花费的时间比脚本执行的时间长。当然,第二次members就填满了。但是有了第一次查询的结果!
此外 var members = [] 应在 printMembers.

内声明

在我看来,我需要一些东西来确保 index() 正在等待 db.each() 完成。但是我怎样才能做到这一点?

(更新:现在您会使用 Koa 2.x,它使用 promises 和 first-class async/await 来代替此答案中的 co/yield 示例。 )

因为 Koa 中的核心抽象 1.x 是使用生成器来控制流程 在路由中,你必须使用 yield 来等待完成。

Koa 在后台使用 https://github.com/tj/co,所以当尝试 与不是为 Koa 构建的库互操作,作为 Koa 开发人员,您 基本上只需要弄清楚如何 modify/wrap 异步库 这样您就可以 yield 他们在您的路线内。

你基本上可以 yield 生成器和承诺。

这项工作最通用的工具是用 一个承诺。创建 Promise (new Promise(fn)) 时,必须将其传递给 函数 fn 以两个函数作为参数:resolvereject.

你经常可以找到 Co-specific 库,其中有人已经包装了一个流行的 图书馆的方式让你 yield 它 functions/methods.

例如,查看 https://www.npmjs.com/package/co-sqlite3。 (我不知道它有多好,但幸运的是,如果结果不好,自己包起来很容易)

或者,有些库可以采用回调式模块(即大多数 Node 模块) 并包装它,以便它 return 承诺您可以 yield.

例如,bluebird has a promisifyAll 执行此操作的函数,但老实说我从未使用过 bluebird。

// (Untested)
var Promise = require('bluebird');
var sqlite3 = require('sqlite3').verbose();
var db = Promise.promisifyAll(new sqlite3.Database(':memory:'));

function* printMembers(name) {
  var query = '...';
  return yield db.allAsync(query, [name]);
}

function* index() {
  // promisifyAll adds an {fnName}Async version of all the functions
  // to the module you call it on, so now we can call `allAsync`.

  var members = yield db.allAsync(query, ['name']);
  // or
  var members = yield printMembers('name');

  this.body = members;
}

但是我会给你一些 Promise-wrapping 的例子,因为它是如此重要 工具包中的工具。

在这里我将你的 printMembers 函数重写为 return 你 然后可以 yield 从你的路线:

var printMembers = function(name) {
  var query = '...';
  return new Promise(function(resolve, reject) {
    var rows = [];
    db.each(query, [name], function(err, row){
      if (err) return reject(err);
      rows.push(row);
    });
    resolve(rows);
  });
}

不过请注意,sqlite3 有一个 Database#all return 一次将所有行发送给您的函数,这样您就不必手动构建数组:

var printMembers = function(name) {
  var query = '...';
  return new Promise(function(resolve, reject) {
    db.all(query, [name], function(err, rows){
      if (err) return reject(err);
      resolve(rows);
    });
  });
}

现在,这是您的路线:

function *index() {
  var members = yield printMembers('name');
  this.body = members;
}

如果承诺命中 reject(err) 路径,那么承诺将被拒绝 state 并会抛出错误,如果你想在 yield 的地方可以 try/catch 承诺。

如果 promise 达到 resolve(members) 路径,那么这就是 yield 并分配给 members 变量。