等到数据库查询完成

cvo*_*igt 1 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');
Run Code Online (Sandbox Code Playgroud)

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

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

在我看来,我需要确定一些事情,那index()就是等到db.each()完成。但我怎样才能做到这一点?

dan*_*neu 5

(更新:这些天你会使用 Koa 2.x,它使用承诺和一流的 async/await 代替这个答案中的 co/yield 示例。)

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

Koa在底层使用https://github.com/tj/co,因此当尝试与不是为 Koa 构建的库进行互操作时,作为 Koa 开发人员,您基本上只需要弄清楚如何修改/包装异步库,以便您可以yield在路线中使用它们。

您基本上可以使用yield生成器和承诺。

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

您经常可以找到特定于 Co-specific 的库,其中有人已经以一种让您可以使用yield其函数/方法的方式包装了一个流行的库。

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

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

例如,bluebird有一个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;
}
Run Code Online (Sandbox Code Playgroud)

但我会为您提供一些 Promise 包装示例,因为它是您工具包中必不可少的工具。

在这里,我重写您的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);
  });
}
Run Code Online (Sandbox Code Playgroud)

不过请注意,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);
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

现在,您的路线如下所示:

function *index() {
  var members = yield printMembers('name');
  this.body = members;
}
Run Code Online (Sandbox Code Playgroud)

如果承诺命中该reject(err)路径,则承诺将处于拒绝状态,并会抛出错误,如果您想要在何处产生承诺,您可以尝试/捕获该错误。

如果承诺命中该resolve(members)路径,那么这就是产生并分配给members变量的内容。