等到数据库查询完成

时间:2016-02-04 23:10:23

标签: javascript sqlite koa

我很确定我的问题与使用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()完成。但是我怎么能实现这个目标呢?

1 个答案:

答案 0 :(得分:3)

由于Koa 1.x中的核心抽象是使用生成器来控制流 在路线中,您必须使用yield等待某些事情完成。

Koa在引擎盖下使用https://github.com/tj/co,所以在尝试时 作为Koa开发人员,您可以与为Koa构建的库互操作 基本上只需要弄清楚如何修改/包装异步库 这样你就可以在你的路线中yield

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

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

你经常可以找到Co-specific库,其中有人已经包装了一个流行的 库以一种允许yield其功能/方法的方式。

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

或者,有些库可以采用回调式模块(即大多数Node模块) 并将其包装,以便它返回你可以yield的承诺。

例如,bluebird有一个promisifyAll功能可以做到这一点,但说实话,我从未使用过蓝鸟。

// (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;
}

但我会给你一些承诺包装的例子,因为它是如此重要 工具箱中有工具。

在这里,我重写你的printMembers函数以返回你的Promise 然后可以从您的路线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 函数会一次性返回所有行,因此您不必手动构建数组:

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)路径,那么承诺将被拒绝 状态并抛出你可以尝试/捕获的错误,如果你想要你屈服的地方 承诺。

如果承诺到达resolve(members)路径,那么那就是产生了什么 并分配给members变量。