我最近从回调函数转移到node.js中的 promises 。我想以最优雅的方式对DB(psql)执行异步查询。我想知道以下代码是否是正确的方法,或者我是否能以first().then(second).then(third)
的方式链接例如承诺。
function queryAll(req, res) {
searchQuery()
.then((rows) => {
console.log(rows);
totalQuery()
.then((total) => {
console.log(total);
});
});
res.json({"rows": rows, "total": total});
}
function searchQuery() {
return new Promise(function(resolve, reject) {
var rowData = { rows: {} };
pool.query('select age, sex from workers;', values, function(err, result) {
if(err) {
return console.error('error running query', err);
reject(err);
}
rowData.rows = result.rows;
resolve(rowData);
});
});
}
function totalQuery() {
return new Promise(function(reject, resolve) {
var totalData = { totals: {} };
pool.query('select sex, scores from workers group by sex;', values, function(err, result) {
if(err) {
return console.error('error running query', err);
reject(err);
}
totalData.totals = result.rows;
resolve(totalData);
});
});
}
答案 0 :(得分:3)
var rowData = { rows: {} };
var totalData = { totals: {} };
首先,这些没有意义存储在变量中,因为对象上没有别的东西。只需直接解决行。
return console.error('error running query', err);
另外,不要只是console.log
您的错误。 then
接受第二个回调,该回调在抛出的错误或被拒绝的承诺上执行。将此消息抛出错误或改为拒绝。此外,我会将记录留给消费者。
function queryAll(req, res) {
searchQuery()
.then((search) => {
console.log(rows);
totalQuery()
.then((total) => {
console.log(total);
});
});
res.json({"rows": rows, "total": total});
}
rows
和total
并不存在于任何地方。另外,在执行res.json
时,rows
和total
(假设它们来自回调内部)已经不存在,因为整个序列都是异步的。您将获得未定义的值作为结果。
我认为按顺序运行searchQuery
和totalQuery
并没有什么意义,因为它们并不相互依赖。相反,并行运行它们会更好。请使用Promise.all
。
function queryAll(req, res) {
Promise.all([
searchQuery(),
totalQuery()
]).then(values => {
const rows = values[0];
const total = values[1];
res.json({"rows": rows, "total": total});
}, function(e){
// something went wrong
});
}
function searchQuery() {
return new Promise(function(resolve, reject) {
pool.query('select age, sex from workers;', values, function(err, result) {
if(err) reject(err);
else resolve(result.rows);
});
});
}
function totalQuery() {
return new Promise(function(reject, resolve) {
pool.query('select sex, scores from workers group by sex;', values, function(err, result) {
if(err) reject(err);
else resolve(result.rows);
});
});
}
答案 1 :(得分:3)
您在代码中遇到一些问题:
return
执行reject()
rows
变量(与search
不匹配)res.json
在结果出现之前执行。{ rows: rows }
之类的对象,但主函数似乎期望普通数字,而不是对象。所以,让承诺只解决数值。group by
子句中。假设您要对分数求和,请使用sum()
。以下是我建议的方法:
function queryAll(req, res) {
return Promise.all([searchQuery(), totalQuery()]).then(([rows, total]) => {
console.log('rows', rows);
console.log('total', total);
// Make sure to only access the promised values in the `then` callback
res.json({rows, total});
});
}
function searchQuery() {
return promiseQuery('select age, sex from workers;');
}
function totalQuery() {
// When you group, make sure to aggregate:
return promiseQuery('select sex, sum(scores) as scores from workers group by sex;');
}
function promiseQuery(sql) { // reusable for other SQL queries
return new Promise(function(resolve, reject) {
pool.query(sql, values, function(err, result) {
if(err) {
// Do not return before calling reject!
console.error('error running query', err);
reject(err);
return;
}
// No need for a variable or object, just resolve with the number of rows
resolve(result.rows);
});
});
}
答案 2 :(得分:1)
最优雅的解决方案是通过pg-promise:
function queryAll(req, res) {
db.task(t => {
return t.batch([
t.any('SELECT age, sex FROM workers', values),
t.any('SELECT sex, scores FROM workers GROUP BY sex', values)
]);
})
.then(data => {
res.json({rows: data[0], total: data[1]});
})
.catch(error => {
// handle error
});
}
这就是一切。您不必重新发明使用数据库的承诺模式,它们都是库的一部分。
如果您的查询有依赖关系,请参阅:How to get results from multiple queries at once。
或者如果您更喜欢ES6发电机:
function queryAll(req, res) {
db.task(function* (t) {
let rows = yield t.any('SELECT age, sex FROM workers', values);
let total = yield t.any('SELECT sex, scores FROM workers GROUP BY sex', values);
return {rows, total};
})
.then(data => {
res.json(data);
})
.catch(error => {
// handle error
});
}
使用ES7的await/async
它几乎是一样的。
答案 3 :(得分:0)
首先,您的代码中存在一些错误,您必须在返回之前放置reject
,否则将永远不会调用它,并创建一个悬空的承诺:
function searchQuery() {
return new Promise(function(resolve, reject) {
var rowData = {
rows: {}
};
pool.query('select age, sex from workers;', values, function(err, result) {
if (err) {
reject(err);
console.error('error running query', err);
} else {
rowData.rows = result.rows;
resolve(rowData);
}
});
});
}
除此之外,你不应该尽可能地嵌套Promise。
所以它应该是:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
});
}
必须在承诺的res.json
部分调用then
:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
})
.then(() => {
res.json({
"rows": result.rows,
"total": result.total
});
});
}
如果您的queryAll
被称为例如Express的中间件,那么你应该处理catch
中的queryAll
案例:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
})
.then(() => {
res.json({
"rows": result.rows,
"total": result.total
});
})
.catch( err => {
res.status(500).json({error: 'some error'})
});
}
对于postgress,我建议使用pg-promise而不是使用回调样式库并自己将其包装到promises中。
如果您使用bluebird这样的库,则可以简化代码:
const bPromise = require('bluebird')
function queryAll(req, res) {
bPromise.all([
searchQuery(),
totalQuery()
])
.spread((rows, total) => {
res.json({
"rows": rows,
"total": total
});
})
.catch(err => {
res.status(500).json({
error: 'some error'
})
});
}
答案 4 :(得分:-1)
使用nsynjs,您的逻辑可以编码为如此简单:
var resp = {
rows: dbQuery(nsynjsCtx, conn, 'select age, sex from workers', values1).data,
total: dbQuery(nsynjsCtx, conn, 'select sex, scores from workers group by sex', values2).data
};
请在此处查看多个顺序查询的示例:https://github.com/amaksr/nsynjs/tree/master/examples/node-mysql