从Redis返回数据仅仅是第二次
我正在使用Redis数据库并为api表示路由。 也使用node.js和ioredis。
我连接到Redis,然后获取所有与inputdate相关的密钥,然后获取与每个密钥相关的所有数据。
但是对我来说奇怪的是,这一切都是有效的,但只是在请求它的第二次刷新页面上。 所以在第一次加载页面(比如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);
你有一个问题,这是asynchronous。
我也怀疑,在每个后续的请求,你会得到你在前一个请求的数据。 另外如果你有多个用户,他们可能会得到别人的请求。
关键问题:您的theData
超出了getData
函数的范围。 另一个关键问题 – 你同步解决这个问题。
简化你的代码:
var theData = []; function getData (date) { redis.on('ready', handler); return theData; }
那么,先调用algorithm:
- 将数据设置为[]
- 设置一些
handler
来等待redis
ready事件。 - 返回
theData
(即[]); - 稍后(当redis准备就绪时),你的处理程序将
theData
设置为结果。
第二个电话:
- 您不要设置数据 – 仅在您的模块第一次加载。 这意味着,数据是你最后设置的(这是前一个请求的数据,由
handler
设置)。 - 再次安排
handler
来做一些事情。 - 返回
theData
(现在保存上一次调用的数据)。 - 处理程序稍后将数据设置为新数据。
等等
基本上是asynchronous问题。
编辑:您的示例代码更新后:
所以随着你的代码更新,你必须做这样的事情:
getData函数应该是asynchronous的(返回一个Promise或使用callback)。 和你的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
需要返回一个Promise。
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
也需要是asynchronous的,再次用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); }); }
这应该让你开始,希望你能从这里工作。