0

ノードの実験を始めたばかりです(Expressを使用して、MySqlデータベースを備えた単純なWebサイトを構築しています)。

私は基本的に、Express が提供するアプリケーション構造で実行しました (これは、この質問には関係ありません)。ホームページへのリクエストが行われるたびにヒットする関数routes/index.jsをエクスポートするファイルがあります。indexの内容は次のindex.jsとおりです。

var db = require('../db');                                                      

exports.index = function(req, res){                                             
    db.getConnection(function(err, connection) {                                
        connection.query('SELECT * FROM test_table', function (err, rows) {     
            var templateVariables = {                                           
                title: 'Index Page',                                            
                response: rows[0].text                                          
            };                                                                  
            res.render('index', templateVariables);                             
        });                                                                     
        connection.end();                                                                                     
    });                                                                                                       
};   

これは明らかに非常に予備的で軽量な例GETですが、インデックス ページに対するこの特定の要求には、すでに 3 つの深さのコールバック関数のセットがあります。各コールバックは、結果に依存するため、「親」のコールバック内に存在する必要があります (順次実行される言語/環境では、これは明白で些細なことです)。

私の質問は、より複雑で潜在的に非常に大規模なアプリケーションを構築する場合、コールバック関数の大量のネストの問題をどのように回避するかということです。これはもちろん、ロジックに順次依存している場合に当てはまります。Node の哲学が非同期であることは知っていますが、データベースからのデータを待機することになると、5 つの個別のクエリを実行しているとしたらどうでしょうか。単一の複数ステートメントのクエリをアトミック ユニットとして記述しますか? この質問はデータベースに限定されませんが。

4

4 に答える 4

2

この問題に関する一般的な議論がここにあります: http://callbackhell.com/

また、多くの人が Async などのモジュールを使用してフロー制御の問題を管理しています。

于 2013-03-24T02:51:22.467 に答える
2

Express を使用して言及したのでnext()、コールバックの代わりに使用できます。

app.get('/',function first(req,res,next){
   res.write('Hello ');
   res.locals.user = req.user;
   next();
   //Execute next handler in queue
});

app.get('/',function second(req,res,next){
   res.write('World!');
   //Use res.locals.user
   res.end();
});

//response shows Hello World!

ルート ハンドラーは追加のパラメーターnextを使用し、いずれかが応答を返すまで、指定された順序で実行されます。nextパラメータをまったく取らないか、エラーをパラメータとして取ります。次の関数に渡したい変数をres.locals

于 2013-03-24T04:28:15.617 に答える
1

Promiseまたは(npm で利用可能)Futureなどのライブラリを使用します。Q

Q の readme から引用すると、promise を使用すると次のようになります。

step1(function (value1) {
    step2(value1, function(value2) {
        step3(value2, function(value3) {
            step4(value3, function(value4) {
                // Do something with value4
            });
        });
    });
});

これに:

Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
    // Do something with value4
}, function (error) {
    // Handle any error from step1 through step4
})
.done();

コールバック地獄に対して私が見た他のすべてのソリューションは、単に後退しているように見えるトレードオフを導入しています。非同期操作は、大規模な操作間の自然な論理境界を形成しないため、関数を因数分解するか、これらの境界に沿ってモジュール化すると、マイクロファクタ化されたコードが得られます。

于 2013-03-24T02:52:56.450 に答える
1

私が好きな方法の1つは、このようなものです...

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

  db.getConnection(gotConnection)

  function gotConnection(err, _c) {
    connection = _c                                
    connection.query('SELECT * FROM test_table', gotData)
  }

  function gotData(err, rows) { 
    connection.end();        
    var templateVariables = {                                           
      title: 'Index Page', 
      response: rows[0].text     
    }
    res.render('index', templateVariables);
  }
});

また、コード内のエラーも常に処理する必要があります。ここでコードを読みやすくするためにエラーを省略したと思います。

于 2013-03-24T03:47:29.923 に答える