5

鉴于 mongoose(或 sequelize 或 redis)查询的异步性质,当您需要在渲染视图之前进行多个查询时,您会怎么做?

例如,您有user_id一个会话,并希望通过 检索有关该特定用户的一些信息findOne。但是您还想显示最近登录用户的列表。

exports.index = function (req, res) {
    var current_user = null

    Player.find({last_logged_in : today()}).exec(function(err, players) {
        if (err) return res.render('500');

        if (req.session.user_id) {
            Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
                if (err) return;
                if (player) {
                    current_user = player
                }
            })
        }

        // here, current_user isn't populated until the callback fires 
        res.render('game/index', { title: 'Battle!',
                   players: players,
                   game_is_full: (players.length >= 6),
                   current_user: current_user
        });
    });
};

res.render在第一个查询回调中也是如此,很好。但是等待响应findOne以查看我们是否认识该用户呢?它只是有条件地调用,所以我不能放在render内部回调中,除非我为任一条件复制它。不漂亮。

我可以想到一些解决方法 -

  • 让它真正异步并在客户端使用 AJAX 来获取当前用户的个人资料。但这似乎比它的价值更多。

  • 使用Q并承诺findOne在渲染之前等待查询的解决。但在某种程度上,这就像强制阻止以使响应等待我的操作。似乎不对。

  • 使用中间件函数获取当前用户信息。这看起来更干净,使查询可重用。但是我不确定如何去做,或者它是否仍然会出现同样的问题。

当然,在更极端的情况下,如果您要进行十几个查询,事情可能会变得很糟糕。那么,考虑到这种类型的要求,通常的模式是什么?

4

3 回答 3

5

是的,这是异步代码中特别烦人的情况。您可以做的是将必须复制的代码放入本地函数中以使其保持干燥:

exports.index = function (req, res) {
    var current_user = null

    Player.find({last_logged_in : today()}).exec(function(err, players) {
        if (err) return res.render('500');

        function render() {
            res.render('game/index', { title: 'Battle!',
                       players: players,
                       game_is_full: (players.length >= 6),
                       current_user: current_user
            });
        }

        if (req.session.user_id) {
            Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
                if (err) return;
                if (player) {
                    current_user = player
                }
                render();
            })
        } else {
            render();
        }
    });
};

然而,看看你在这里做什么,你可能需要在多个请求处理程序中查找当前玩家信息,所以在这种情况下你最好使用中间件。

就像是:

exports.loadUser = function (req, res, next) {
    if (req.session.user_id) {
        Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
            if (err) return;
            if (player) {
                req.player = player
            }
            next();
        })
    } else {
        next();
    }
}

然后,您将配置路由以调用loadUser您需要req.player填充的任何位置,并且路由处理程序可以直接从那里提取玩家详细信息。

于 2012-11-06T03:03:15.233 回答
1
router.get("/",function(req,res){
    var locals = {};
    var userId = req.params.userId;
    async.parallel([
        //Load user Data
        function(callback) {
             mongoOp.User.find({},function(err,user){
                if (err) return callback(err);
                locals.user = user;
                callback();
            });
        },
        //Load posts Data
        function(callback) {
                mongoOp.Post.find({},function(err,posts){
               if (err) return callback(err);
                locals.posts = posts;
                callback();
            });
        }
    ], function(err) { //This function gets called after the two tasks have called their "task callbacks"
        if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
        //Here `locals` will be an object with `user` and `posts` keys
        //Example: `locals = {user: ..., posts: [...]}`
         res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
    });
于 2017-03-10T09:45:41.873 回答
0

现在,您可以使用 ExpressJS 中的 app.param 轻松建立中间件,根据请求 URL 中的参数名称加载所需的数据。

http://expressjs.com/4x/api.html#app.param

于 2014-10-08T15:12:55.150 回答