0

コールバックの在庫を減らすためにデータベース呼び出し (sqlite3) を実行するループがありました。それは正常に機能し、「使用済みの部分」が 1 減りました。

平均して 1 から約 60 の配列要素が存在する可能性があるため、このループは 1 から 60 の非同期タスクを立て続けに作成します。コールバック関数のシグネチャは、sqlite3 への JS バインディングによって決定される (エラー、行) です。変更できません。

     i = 0;
     while (i < struct.PortionsUsed.length) {
        portionUsed = struct.PortionsUsed[i];
        Db.get("SELECT blah blah WHERE portion = ? ORDER BY date LIMIT 1", portionUsed, selectCallBack);
        i++;
     }

配列には多くの重複要素が含まれており、配列を並べ替えると、もちろん、それらすべてが互いに隣接して配置されるため、もう少しロジックを使用して、1 回の呼び出しで在庫から差し引く "portionUsed" アイテムの数のカウントを作成できます。 60 のタスクを約 10 に減らすと、実行する価値があります。これは、コールバックにカウントを与える必要がありますが、それを提供する一般的なメカニズムがないことを意味します。

コールバックが起動するまでに、カウントはループ内の最後のカウントの値になるため、クロージャーは機能しません (試してみました)。コールバック内で使用できるように、コールバック タスクの作成時に現在の「カウント」を提供する必要があったため、コールバック関数オブジェクトのプロトタイプ拡張機能を使用して、関数ごとに一意の「カウント」をドラッグしようとしました。 Db 呼び出しごとにインスタンス化されたコールバック関数。

     struct.PortionsUsed = struct.PortionsUsed.sort();  // Get all identical portion items adjacent to each other.
     i = 0;
     while (i < struct.PortionsUsed.length) {
        // i points at the first portion item, whatever it is.
        j = i + 1;
        while (j < struct.PortionsUsed.length && struct.PortionsUsed[i] === struct.PortionsUsed[j]) {
           ++j;
        }
        // j points at 1 past the last identical portion item.
        count = j - i; // count has the number of portions to deduct from inventory.

        // Get the oldest Portion row and reduce the qty by the count.
        portionUsed = struct.PortionsUsed[i];
        if (debug) {console.log('Starting Select for ' + portionUsed + ' - count=' + count);}
        selectCallBack.prototype.count = count;
        selectCallBack.prototype.portionUsed = portionUsed;
        Db.get("SELECT rowid AS rowNum, portion, qty FROM Portion WHERE portion = ? ORDER BY date LIMIT 1", portionUsed, new selectCallBack);
        //Db.get("SELECT rowid AS rowNum, portion, qty FROM Portion WHERE portion = ? ORDER BY date LIMIT 1", portionUsed,
        //   function(error, row) {count; portionUsed; selectCallBack(error, row);});
        i = j;
     }

(エラー、行) の両方が未定義であるため、コールバックはまったく機能しません。なんで?どうすればこれを修正できますか? コールバック内では、this.count が思い通りに利用できます。

これについてもっと良い方法はありますか?

コールバックは次のとおりです。

     function selectCallBack(error, row) {
        var count = this.count;                // made active ONLY when attempting to use prototype
        var portionUsed = this.portionUsed;    // made active ONLY when attempting to use prototype
        var portion;

        if (debug) {console.log('Hit selectCallBack. count=' + count + ' portionUsed=' + portionUsed);}
        if (debug) {console.log(typeof error + typeof row);}
        if (error !== null) {
           if (debug) {console.log('selectCallBack error:\n' + error);}
           success = false;
        } else {
           // real work goes here
        }
     }     // no return statement of any kind.

閉鎖出力:

   Starting Select for Coffee - count=5
   Starting Select for Hot Tea - count=2
   Hit selectCallBack. count=2 portionUsed=Hot Tea
   objectobject
   Hit selectCallBack. count=2 portionUsed=Hot Tea
   objectobject

プロトタイプ アプローチの出力:

   Starting Select for Coffee - count=5
   Hit selectCallBack. count=5 portionUsed=Coffee
   undefinedundefined
   selectCallBack error:
   undefined
   Starting Select for Hot Tea - count=2
   Hit selectCallBack. count=2 portionUsed=Hot Tea
   undefinedundefined
   selectCallBack error:
   undefined
4

1 に答える 1

0

あなたの閉鎖の試みは、JavaScript で新しい変数スコープを作成する唯一の方法は関数を呼び出すことであるという事実に苦しんでいます。ループ内で関数を作成している間、すべての関数が同じ変数スコープで作成されています。

これを解決するには、ループの反復ごとに関数を呼び出し、その新しい変数スコープに保持する必要がある値を渡し、同じスコープでコールバック関数を作成する必要があります。このようにして、ループで作成された各関数は、一意の関数スコープで作成されます。

IMOがこれを行う最も明確な方法は、スコープを必要とするコードを新しい関数に移動し、ループで呼び出すことです。

 struct.PortionsUsed = struct.PortionsUsed.sort();
 i = 0;
 while (i < struct.PortionsUsed.length) {
    j = i + 1;
    while (j < struct.PortionsUsed.length && struct.PortionsUsed[i] === struct.PortionsUsed[j]) {
       ++j;
    }
    count = j - i;
    portionUsed = struct.PortionsUsed[i];
    if (debug) {console.log('Starting Select for ' + portionUsed + ' - count=' + count);}

    setHandler(Db, count, portionUsed, selectCallback);

    i = j;
 }

function setHandler(Db, count, portionUsed, callback) {
    // In here, any of the above parameters can be used within the 
    //    handler function created below.
    Db.get("SELECT rowid AS rowNum, portion, qty FROM Portion WHERE portion = ? ORDER BY date LIMIT 1", 
           portionUsed, 
           function(error, row) {
               console.log(count, portionUsed); // Not sure how you wanted to use these
               callback(error, row);
           }
    );
}

同様の解決策は、新しい関数でのみハンドラーを作成し、関数がそれを返すようにすることです。

 struct.PortionsUsed = struct.PortionsUsed.sort();
 i = 0;
 while (i < struct.PortionsUsed.length) {
    j = i + 1;
    while (j < struct.PortionsUsed.length && struct.PortionsUsed[i] === struct.PortionsUsed[j]) {
       ++j;
    }
    count = j - i;
    portionUsed = struct.PortionsUsed[i];
    if (debug) {console.log('Starting Select for ' + portionUsed + ' - count=' + count);}

    Db.get("SELECT rowid AS rowNum, portion, qty FROM Portion WHERE portion = ? ORDER BY date LIMIT 1", 
           portionUsed,
           setHandler(count, portionUsed, selectCallback)
    );

    i = j;
 }

function setHandler(count, portionUsed, callback) {
    // In here, any of the above parameters can be used within the 
    //    handler function that we create and return below.
    return function(error, row) {
       console.log(count, portionUsed); // Not sure how you wanted to use these
       callback(error, row);
    };
}

ループ内でインラインの即時呼び出し関数を使用してこれを行うのが好きな人もいますが、コードが乱雑になりすぎると思います。名前付き関数を使用することを好みます。

于 2013-09-05T18:56:39.097 に答える