2015-05-25 3 views
1

У меня возник вопрос о выходе следующего кода.переменная set для последнего значения in for loop в Javascript

var _list = [{id:0}, {id:1}, {id:2}, {id:3}, {id:4}]; 

function storeList() { 
    for (var i = 0, j = _list.length; i < j; i++) { 
     var key = makeKey(_list[i].id); 
     _db.setValue(
      function() { 
       console.log("OK: store a value of " + key); 
      }, 
      function() { 
       throw "ERR: can't store a value of " + key; 
      }, 
      databaseName, 
      key, 
      _list[i]); 
    } 
} 

storeList(); 

Я ожидаю, что он должен вывести:

OK: store a value of 0 
OK: store a value of 1 
OK: store a value of 2 
OK: store a value of 3 
OK: store a value of 4 

Однако, она выводит:

OK: store a value of 4 
OK: store a value of 4 
OK: store a value of 4 
OK: store a value of 4 
OK: store a value of 4 

Почему? и, каков правильный способ вывода? Я запустил этот код javascript на Android Webview.

Заранее спасибо.

ответ

3

Вам необходимо сделать закрытие. Вы можете сделать это, обернув код после создания ключа в себя выполнение функции (IIFE)

(function(key) { 
    db.setValue(
     function() { 
      console.log("OK: store a value of " + key); 
     }, 
     function() { 
      throw "ERR: can't store a value of " + key; 
     }, 
     databaseName, 
     key, 
     _list[i] 
    ); 
})(key); 

Это потому, что объем key до того, как видно к все ваши итерированные setValue звонки. Используя IIFE, ваша область переданных ключей является «внутри» каждого вызова setValue.

+0

спасибо. На самом деле, примерно, я думал, что причина может быть такой, но я подумал, что, возможно, Javascript-движок разумен, чтобы справиться с этим для разработчиков. –

1

db.setValue - асинхронный метод. Поэтому, когда вы его вызываете, он отделяется от основного потока программы, сохраняя при этом свой доступ к счетчику контуров i. Javascript всегда выполняет текущий поток перед выполнением любого асинхронного кода. Таким образом, ваш цикл работает 4 раза, каждый раз создавая блок асинхронного кода, который может запускаться только после завершения текущей исполняемой программы (для цикла).

Обратите внимание, что каждый асинхронный блок имеет доступ к тому же i. Поэтому, когда настало время для их выполнения, все, что они видят, - это значение i, которое существует после завершения основного потока (что в данном случае равно 4).

Учитывая это, самый простой способ справиться с этими проблемами - создать закрытие. В принципе, вы дадите каждому асинхронному блоку копию i, которая останется на том значении, которое было при создании блока (если ваш асинхронный блок не изменил его). Вы можете сделать это с помощью iife, как описано в @AmmarCSE. Более чистый способ подхода - переместить этот материал в метод.

function setValue (i) { 
    var key = makeKey(_list[i].id); 
    _db.setValue(
     function() { 
      console.log("OK: store a value of " + key); 
     }, 
     function() { 
      throw "ERR: can't store a value of " + key; 
     }, 
     databaseName, 
     key, 
     _list[i] 
    ); 
}; 

function storeList() { 
    for (var i = 0, j = _list.length; i < j; i++) { 
     setValue(i); 
    } 
} 
+0

Спасибо. Вы пошаговое объяснение очень ясно! –

Смежные вопросы