最近发现了 JS promises ,I have been studying them以便我可以构建一个允许我执行4个异步查询的特定功能,使用每个的结果构建一个我可以的对象最后发送作为对我的节点应用程序的请求的响应。
但是,似乎我在处理承诺时做错了,因为最终,game
没有被构建。它作为空对象发送。 Here's a JSFiddle.
我的错误是什么?
这是我到目前为止所拥有的:
function sendGame(req, res, sales, settings, categories) {
var game = new Object();
game.sales = sales;
game.settings = settings;
game.categories = categories;
JSONgame = JSON.stringify(game);
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost',
'Content-Length': JSONgame.length,
'Content-Type': 'application/json'
});
res.write(JSONgame);
res.end();
console.log('Game: ' + JSON.stringify(game, null, 4));
console.log('--------------------------------------');
console.log('User ' + req.body.username + ' successfully retrieved game!');
}
function retrieveSales(req, connection, timeFrame) {
console.log('User ' + req.body.username + ' retrieving sales...');
connection.query('select * from sales_entries where date BETWEEN ? AND ?', timeFrame,
function (err, rows, fields) {
if (err) {
callback(new Error('Failed to connect'), null);
} else {
sales = [];
for (x = 0; x < rows.length; x++) {
sales.push(rows[x]);
}
//console.log('Sales: ' + JSON.stringify(sales, null, 4));
return sales;
}
});
}
为了便于阅读,省略了 retrieveCategories()
和retrieveSettings()
;它们与retrieveSales()
大致相同。
function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).format("YYYY-MM-DD HH:mm:ss")];
var connection = createConnection();
connection.connect(function (err) {
if (err) return callback(new Error('Failed to connect'), null);
console.log('Connection with the Officeball MySQL database openned for game retrieval...');
var sales = retrieveSales(req, connection, timeFrame);
var settings = retrieveSettings(req, connection);
var categories = retrieveCategories(req, connection);
var all = q.all([sales, settings, categories]);
all.done(function () {
sendGame(req, res, sales, settings, categories);
});
});
}
答案 0 :(得分:4)
你的问题是你没有使用承诺。您的所有API都使用回调。
承诺就像一个封闭的盒子:
承诺还有一个打开框的方法,对值进行处理并返回值上的另一个框(同时打开任何其他框)。该方法为.then
:
在框中,它确实:
=&gt;(。=&gt; )=&gt;
也就是说,它添加了一个获取打开框并返回一个框的处理程序。其他一切只是结合了一些东西。所有.all
都会等待一个承诺列表来解决,它等待结果就像.then
一样。因为承诺是盒子,你可以传递它们并返回它们非常酷。
一般而言:
所以基本上在节点中说:
所以:
function myFunc(callback){
nodeBack(function(err,data){
if(err!== null){
callback(new Error(err),null);
}
callback(data+"some processing");
})
});
变为:
function myFunc(){
return nodeBack().then(function(data){ return data+"some processing"; });
}
我认为更清楚。错误通过promise链传播,就像在同步代码中一样 - 找到同步模拟以承诺代码是很常见的。
Q.all
会获取承诺列表并等待它们完成,而您希望Q.nfcall
将基于回调的API转换为承诺API,然后使用Q.all
。< / p>
那是:
var sales = Q.nfcall(retrieveSales,req, connection, timeFrame);
var settings = Q.nfcall(retrieveSettings,req, connection);
var categories = Q.nfcall(retrieveCategories, req, connection);
Q.nfcall
在err,data
约定中获取一个nodeback并将其转换为promise API。
另外,当你做
时return sales;
你并没有真正返回任何东西,因为它同步返回。您需要在错误案例中使用callback
,或者完全宣传它。如果你不介意的话,我会用Bluebird做的,因为它有更好的工具来处理这些互操作情况并且速度快得多,如果你希望你可以切换promisifyAll
一堆Q.nfcall
来电。
// somewhere, on top of file
connection = Promise.promisifyAll(connection);
// note I'm passing just the username - passing the request breaks separation of concerns.
var retrieveSales = Promise.method(username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function(rows, fields){
return rows;
});
}
请注意,突然您不需要很多样板来进行查询,如果您愿意,可以直接使用queryAsync。
现在包装它的代码变为:
var gameSucceed = Promise.method(function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment()....];
var connection = Promise.promisifyAll(createConnection());
return conn.connectAsync().then(function () {
console.log('Connection with the ...');
//sending req, but should really be what they use.
return Promise.all([retrieveSales(req,conn,timeFrame),
retrieveSettings(req,conn),
retrieveCategories(req,conn)]);
});
});
现在你可以在gameSucceed之外调用sendGame(req, res, sales, settings, categories);
,而不会隐藏它的功能 -
gameSucceed(req,res).spread(function(sales,settings,cats){
return sendGame(req,res,sales,settings,cats);
});