2013-09-05 2 views
0

У меня был цикл, который вызывал вызовы базы данных (sqlite3), чтобы уменьшить количество ресурсов в обратном вызове. Он отлично работал, уменьшая все, что у «partUsed» было на 1.Использование прототипа для добавления свойства к функции приводит к тому, что параметры функции становятся неопределенными

В среднем может быть от 1 до 60 элементов массива, поэтому этот цикл будет создавать от 1 до 60 задач асинхронной работы в быстрой последовательности. Подпись функций обратного вызова (ошибка, строка), определяемая привязкой 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++; 
    } 

В containes массива много дубликатов элементов и сортировка массива будет, конечно, разместить их все рядом друг с другом так, что немного больше логики может создать подсчет количества предметов «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 
+0

* «Закрытие не будет работать (проверено), потому что по времени срабатывания обратных вызовов счетчик является значением последнего счета в цикле.» * ... Затем вы делали это неправильно. – user2736012

+1

Когда вы изменяете 'selectCallBack.prototype.count' в цикле, все объекты, созданные из' new selectCallBack() ', будут видеть обновление. Другими словами, объект '.prototype' разделяется между всеми экземплярами, и поэтому все они наблюдают обновления. Почему бы просто не поместить свойство непосредственно на объект, который вы создаете? – user2736012

+0

... если третий аргумент 'DB.get()' должен быть функцией обратного вызова, вы не передаете его, когда вы делаете 'new selectCallback', если один из них не возвращается из' selectCallback' при его вызове. – user2736012

ответ

0

Ваша попытка закрытия страдает от того, что единственный способ, чтобы создать новая переменная scope в JavaScript должна вызывать функцию. Пока вы создаете функцию в цикле, все функции создаются в той же области переменных.

Чтобы исправить это, вам нужно вызвать функцию на каждой итерации цикла, передать значение, которое необходимо сохранить в этой новой области видимости, и создать функцию обратного вызова в той же области видимости. Таким образом, каждая функция, созданная в цикле, создается в уникальной области функций.

Самый ясный способ, с помощью которого ИМО должен переместить код, который нуждается в области, в новую функцию и вызвать его в цикле.

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

Некоторых люди любят делать это с помощью встроенного, немедленно вызываемой функции непосредственно в цикле, но я думаю, что это загромождает код слишком много. Я предпочитаю использовать именованные функции.

+0

Вы установили меня на правильный путь, как только я увидел, что вы используете выход функции для возврата функции. Это то, чего мне не хватало. Я бы никогда об этом не думал. Спасибо! –