2012-11-06 13 views
5

mongoose(またはsequelize、redis)クエリの非同期性を指定すると、ビューをレンダリングする前に複数のクエリを作成する必要があります?express.jsのビューに複数のDB/mongooseクエリをレンダリングした結果

たとえば、セッションに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クエリの解決を待つことを約束します。しかし、ある意味では、これはブロッキングが強制的に私の操作で応答を待たせるようなものです。正しいとは思わない。

  • 現在のユーザー情報を取得するためにミドルウェア機能を使用します。これはよりクリーンで、クエリを再利用可能にします。しかし、私はそれについてどうやって行くのか、それでも同じ問題が現れるかどうかはわかりません。

もちろん、より極端な場合には、作成するクエスチョンが十数個あれば、醜いものになる可能性があります。だから、このタイプの要件が与えられたときの通常のパターンは何ですか?

答えて

5

これは、非同期コードで特に厄介なケースです。あなたにできることは、あなたがDRYそれを維持するために、ローカル関数に複製する必要があるだろうコードを配置することです:

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(); 
    } 
} 

その後、あなたはreq.playerが取り込ま必要な場所loadUserを呼び出すようにしてルートハンドラは、ちょうどそこからプレイヤーの詳細を引くことができ、あなたのルートを設定したいです。

+0

おかげで、十分に単純なようです。 'async.parallel'を使ってすべての結果をまとめるのはどうでしょうか?私はそれを試して、それは動作するようだが、それが良いアプローチかどうか疑問に思う。あなたのものは、はるかに難読化されています。 – sbeam

+0

うん、 'async.parallel'はうまくいきません。おそらくあなたのユースケースに適したミドルウェアソリューションを含めるように答えを更新しました。 – JohnnyHK

+0

nice、それは 'next()'との連鎖のおかげで再利用可能できれいです。 – sbeam

0

最近、ExpressJSでapp.paramを使用すると、リクエストURLのパラメータ名に基づいて必要なデータをロードするミドルウェアを簡単に構築できます。

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

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}) 
    }); 
関連する問題