0

それで、私は自分のチャット システムを Node.js にアップグレードしようとしています。

PHP では非効率的ですが、間違いなく理にかなっています。リクエストが起動し、ユーザー情報やルーム情報を把握したり、メッセージを解析したりします。かなり直線的です。さまざまな場所でいくつかの関数を呼び出しますが、そのたびに必要な情報をその関数に直接送信するだけで済みます。その機能が完了すると、元に戻り、別の情報を使用して別の作業が行われます。例外が発生した場合、(通常) ユーザーに警告できるレベルでキャッチされます。

私が理解しているように、Node.js は絶対にこのようには機能せず、代わりに主にコールバックによって駆動されます - そして私は非常に多くのコールバックを行っています。最初の接続を処理する必要があり、Cookie ファイルが存在するかどうかを確認し、Cookie ファイルを読み取って、データベースからユーザー情報を取得する必要があります。データベースから他のユーザー情報を取得し、データベースからさらにユーザー情報を取得する必要があります。次に、ユーザーをルームに追加する必要があります。データベースからルーム情報を取得し、最後にリクエストに応答します。作業が完了すると、少なくとも 2 つの権限チェック レベルが追加されます。

PHP プロセスと大差ありませんが、PHP では Apache を介してマルチスレッド化されているため、リクエストはそこに留まり、DB 呼び出しが返されるのをまったく問題なく待つことができます。ユーザー ルックアップ、ルーム サブスクリプション、アクセス許可、すべてが個別に処理されます。

Node.js では、「作業が完了したら」というシステムは、頭を包み込むのにそれほど難しくはありません (私はクライアント側の JS と jQuery を十分に使用しました) が、変数の受け渡しは確かに困難です。これの大きな部分は、try/catch がコールバックによって完全に打ち負かされることです。ルーム データ ルックアップ クエリが失敗した場合、その関数は、エラーを送り返す必要がある接続を知る必要があります (過去には 2 つまたは 3 つの接続である可能性があります)。 . そのため、途中ですべてのコールバックを介して接続オブジェクトを渡す必要があります。例外はおそらくどこでも発生する可能性があるため、例外を処理するときに少しうんざりするだけです。最後に1 つのコールバックがあり、何がひどく間違っているのかを調べるまで、私の指はそれ以上入力することを拒否します!

だから私が疑問に思っているのは、変数がネストされていないコールバックを「ジャンプ」できるようにする、なじみのない「ハック」があるかどうかということです。try/catch チェーンを無期限にダウンさせることも気の利いたものです。

編集:何百行ものコードを矮小化するのに問題があるので、コールバック スタックで視覚的な支援を提供できるかどうか見てみましょう。同じ行にあるものはすべて直接呼び出しで、次の行はコールバックです。

connection.on('messaage') -> controller.validateUser -> fs.exists
 fs.readFile
  function() -> controller.addUser -> factory.user -> user.refreshData -> db.query
   user.refreshChars -> db.query
    user.refreshBlocks -> db.query
     function() -> controller.addRoom -> factory.room -> room.refreshData -> db.query
      room.getRole -> db.query
       function() -> room.getUserList -> connection.sendUTF

ご覧のように、関数はネストされた名前のない関数ではなく、ほとんどがオブジェクト内に配置されています。これは、多くの場合、複数の場所から任意の順序でアクセスする必要があるためです。問題は、ユーザー オブジェクトを必要とするレベルと必要としないレベルがあることです。try/catch が適切に機能していれば、最初と最後だけが接続を認識して情報を送り返す必要があります。

私が必要としているのは、これらのさまざまな関数にさまざまな情報を与える方法であり、その前にすべての関数を必要のないものであふれさせる必要はありません。それは望ましくない慣習です。また、さまざまなユーザー オブジェクト関数がさまざまな方法で失敗することも必要です。これは、呼び出し元の関数の責任であるため、オブジェクトが自分自身に関与する必要がない方法です。

4

1 に答える 1

0
initial(); // begins the process


  // this starts things off
function initial() {
    var props = {  // this is the common object
        onerror: function(err) {
                     if (err.msg === "reallyBadError")
                         return false; // false means stop
                     else
                         return true;  // true means we can continue
                 },
        someInitialData: {whatever:"data"}
    };

    doSomethingAsync(getFirstCallback(props));
}


function getFirstCallback(props) {

        // return the actual callback function
    return function(err, info) {
           // if callback was passed an error, handle it
        if (err && props.onerror(err) === false)
            return;

        props.info = info; // add something to props
        doAnotherAsync(getSecondCallack(props));
    };
}


function getSecondCallback(props) {

        // return the actual callback function
    return function(err, foo) {
           // if callback was passed an error, handle it
        if (err && props.onerror(err) === false)
            return;

        // maybe do something with props.info
        props.foo = foo; // add something to props
        doOneMoreAsync(getFinalCallack(props));
    };
}


function getFinalCallback(props) {

        // return the actual callback function
    return function(err, bar) {
           // if callback was passed an error, handle it
        if (err && props.onerror(err) === false)
            return;

        // maybe do something with props.info and props.foo

        // we also have access to the original props.whatever
    };
}

プロトタイプバージョンは次のとおりです。

var r = new Requester(); // begins the process


  // Here's the implementation
function Requester() {
    // "this" is the common object
    this.someInitialData = {whatever:"data"};

    doSomethingAsync(this.firstCallback.bind(this));
}

Requester.prototype.onerror: function(err) {
     if (err.msg === "reallyBadError")
         return false; // false means stop
     else
         return true;  // true means we can continue
 };

Requester.prototype.firstCallback = function(err, info) {
       // if callback was passed an error, handle it
    if (err && this.onerror(err) === false)
        return;

    this.info = info; 
    doAnotherAsync(this.secondCallack.bind(this));
};


Requester.prototype.secondCallback = function(err, foo) {
       // if callback was passed an error, handle it
    if (err && this.onerror(err) === false)
        return;

    this.foo = foo;
    doOneMoreAsync(this.finalCallack.bind(this));
};


Requester.prototype.finalCallback = function(err, bar) {
       // if callback was passed an error, handle it
    if (err && this.onerror(err) === false)
        return;

    // The final code    
};
于 2012-12-12T01:30:42.347 に答える