0

我有一个循环执行数据库调用(sqlite3)以减少回调中的库存。它工作得很好,将“使用的部分”减少了 1。

平均可能有 1 到大约 60 个数组元素,因此这个循环将快速连续创建 1 到 60 个异步任务。回调函数签名是 (error, row) 由 JS 绑定到 sqlite3 确定。我无法改变它。

     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++;
     }

该数组包含许多重复的元素,并且对数组进行排序当然会将它们彼此相邻放置,以便更多的逻辑可以创建一个计数“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;
     }

回调现在根本不起作用,因为 (error, row) 都未定义。为什么?我怎样才能解决这个问题?在回调中, 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 回答