从Redis返回数据仅在第二次工作

时间:2016-01-26 13:18:59

标签: javascript node.js express redis

我正在使用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);

1 个答案:

答案 0 :(得分:2)

你有这个异步的问题。

我还怀疑在每个后续请求中,您将获得您在上一个请求中请求的数据。此外,如果您有多个用户,他们可能会收到其他人的请求。

关键问题:您的theData超出了getData功能的范围。 另一个关键问题 - 您可以同步解决此问题。

简化您的代码:

var theData = [];
function getData (date) {
    redis.on('ready', handler);
    return theData;
}

所以,首先调用算法:

  1. theData设为[]
  2. 设置一些handler以等待redis 准备好事件。
  3. 返回theData(即[]);
  4. 稍后(当redis准备就绪时),处理程序会将theData设置为结果。
  5. 第二个电话:

    1. 您没有设置数据 - 仅在您第一次加载模块时。 意思是,theData是您上次设置的数据(来自上一个请求的数据,由handler设置。
    2. 再次安排handler准备事件上执行某些操作。
    3. 返回theData(现在保留上次通话中的数据)。
    4. 处理程序稍后会将数据设置为新数据。
    5. 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);
        });
      }
      

      这应该让你开始,希望你可以在这里工作。