【问题标题】:Wait until DB query is done等到数据库查询完成
【发布时间】:2016-02-04 23:10:23
【问题描述】:

我很确定我的问题与 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() 完成。但是我怎样才能做到这一点呢?

【问题讨论】:

    标签: javascript sqlite koa


    【解决方案1】:

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

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

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

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

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

    你经常可以找到一些特定的库,其中有人已经包装了一个流行的 库,让您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;
    }
    

    但我会给你一些 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);
      });
    }
    

    虽然请注意 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;
    }
    

    如果 Promise 命中 reject(err) 路径,则 Promise 将被放入拒绝 状态并抛出错误,如果你想在哪里产生,你可以尝试/捕获 承诺。

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

    【讨论】:

    • 哇,这是一个完美的答案! a)现在我知道我错过了哪块 JS 乐高积木,b)你提供了多种解决方案,c)你甚至给出了一个可行的例子。非常感谢!当您遇到问题但不知道要搜索什么时,这很难!
    猜你喜欢
    • 1970-01-01
    • 2021-03-23
    • 2020-02-11
    • 1970-01-01
    • 2017-10-12
    • 2016-06-09
    • 1970-01-01
    • 2020-03-22
    • 2022-01-21
    相关资源
    最近更新 更多