等到数据库查询完成
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
以两个函数作为参数:resolve
和 reject
.
你经常可以找到 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
变量。
我很确定我的问题与 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
以两个函数作为参数:resolve
和 reject
.
你经常可以找到 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
变量。