将多个DB/mongoose查询的结果呈现给express.js中的视图

sbe*_*eam 5 mongoose node.js express

鉴于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
        });
    });
};
Run Code Online (Sandbox Code Playgroud)

所以res.render在第一个查询回调中,很好.但是等待响应findOne来看看我们是否了解这个用户呢?它只是有条件地调用,所以我不能放入render内部回调,除非我为任何一个条件复制它.不漂亮.

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

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

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

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

当然,在一个更极端的情况下,如果你有十几个查询要做,事情可能会变得丑陋.那么,给定这种要求的通常模式是什么?

Joh*_*yHK 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();
        }
    });
};
Run Code Online (Sandbox Code Playgroud)

但是,看看你在这里做什么,你可能需要在多个请求处理程序中查找当前的播放器信息,所以在这种情况下你最好使用中间件.

就像是:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以将路由配置为loadUser在需要req.player填充的地方进行呼叫,路由处理程序可以直接从那里提取玩家详细信息.