我正在使用Redis数据库并为api表达路由。也使用node.js和ioredis。
我连接到Redis,然后获取与输入日期相关的所有密钥,然后获取与这些密钥相关的所有数据。
但令我感到奇怪的是,这一切都有效,但仅限于请求它的页面的第二次刷新。因此,在第一次加载页面时(比如blah.com/api/20120120),它将返回一个空数组。然后,如果您刷新页面,它将返回正确的数据。
HomeController.js:
var theData = [];
function getData (date) {
redis.on('ready', function (res) {
var keys = [];
var stream = redis.scanStream(
{
match: date + '*',
count: 1000
}
);
stream.on('data', function (resultKeys) {
for (var i = 0; i < resultKeys.length; i++) {
keys.push(resultKeys[i]);
}
});
stream.on('end', function () {
setValues(keys);
});
});
return theData;
}
var setValues = function(keys) {
for (var i = 0; i < keys.length; i++) {
redis.hgetall(keys[i], function (err, result) {
theData.push(result);
})
}
}
var HomeController = Controller.extend({
api: function (date) {
this.setRender();
this.getResponse().json(getData(date));
}
});
Server.js:
app.get("/api/:date", router("HomeController", "api", ["date"]));
任何人都知道为什么会这样?
编辑:添加了controller.js:
(function(module) {
"use strict";
// https://github.com/darlanalves/extends
var extend = require("extends");
var Class = function() {};
var Controller = extend(Class, {
constructor: function(req, resp) {
this._request = req;
this._response = resp;
this._view = null;
this._viewProperties = {};
this._render = true;
this.assign("loggedIn", false);
this.assign("location", req.protocol + '://' + req.get('host') + req.originalUrl);
},
assign: function(key, value) {
this._viewProperties[key] = value;
return this;
},
assignObject: function(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
this.assign(key, obj[key]);
}
}
},
failWith404: function() {
this.getRequest().log({status:404}, "controller 404");
this.getResponse().status(404).render('404.jade');
},
getRequest: function() {
return this._request;
},
getResponse: function() {
return this._response;
},
getView: function() {
return this._view;
},
getViewProperties: function() {
return this._viewProperties;
},
render: function() {
var viewProps = this.getViewProperties(),
logProps = {
loggedIn: viewProps.loggedIn,
};
if (this._render !== true) {
// No rendering, no headers
} else if (this.getView() !== null) {
// Jade
this.getResponse().render(this.getView(), this.getViewProperties());
} else {
// JSON response
this.getResponse().json(this.getViewProperties());
}
this.getRequest().log(logProps, "controller hit");
},
setRender: function(renderEnabled) {
this._render = (renderEnabled === true);
},
setView: function(viewName) {
this._view = viewName;
}
});
module.exports = Controller;
})(module);
答案 0 :(得分:2)
你有这个异步的问题。
我还怀疑在每个后续请求中,您将获得您在上一个请求中请求的数据。此外,如果您有多个用户,他们可能会收到其他人的请求。
关键问题:您的theData
超出了getData
功能的范围。
另一个关键问题 - 您可以同步解决此问题。
简化您的代码:
var theData = [];
function getData (date) {
redis.on('ready', handler);
return theData;
}
所以,首先调用算法:
theData
设为[] handler
以等待redis
准备好事件。theData
(即[]); theData
设置为结果。第二个电话:
handler
设置。handler
在准备事件上执行某些操作。theData
(现在保留上次通话中的数据)。Etc等。
基本上是异步问题。
编辑:示例代码更新后:
因此,在您的代码更新时,您必须执行以下操作:
getData函数应该是异步的(返回Promise或使用回调)。和你的HomeController。
示例:
let HomeController = Controller.extend({
api: function (date) {
this.setRender();
getData(date) // Get data asynchroonously here
.then(function(data) {
this.getResponse().json(data);
})
.catch(function(err) {
// handle error
this.getResponse().status(500).json({message: 'Error getting data');
});
}
});
但是现在有一个问题 - getData
需要返回一个承诺。
let Promise = require('bluebird'); //unless you have Node 0.12+, it has Promise already.
function getData(date) {
return new Promise(function(resolve, reject) {
let theData = []; // initialize empty data for each req
redis.on('ready', function() {
var keys = [];
var stream = redis.scanStream({
match: date + '*',
count: 1000
});
stream.on('data', function (resultKeys) {
for (var i = 0; i < resultKeys.length; i++) {
keys.push(resultKeys[i]);
}
});
stream.on('end', function () {
resolve(setValues(keys)); // Here we `resolve` our promise
});
stream.on('error', reject); // just a minor thing
}
});
}
但现在你的setValues
也需要异步,再次使用Promise:
function setValues(keys) {
// Return a promise to the initial caller.
return new Promise(function(resolve, reject) {
let parallelizedQueries = keys.map(function(key) {
// Now, EACH of the hgetall also needs to be a Promise or somehow async
return new Promise(function(innerResolve, innerReject) {
redis.hgetall(key, function(err, result) {
if (err) {
return innerReject(err);
}
innerResolve(result);
});
});
});
// So now we have asked for each of the keys, in parallel.
// Wait until all of them succeed or fail on the first one.
Promise.all(parallelizedQueries)
.then(function(data) {
// Here is our data.
return resolve(data);
})
.catch(reject);
});
}
这应该让你开始,希望你可以在这里工作。