我对以下代码的输出有疑问。
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
为什么呢?什么是正确的输出方式? 我在Android Webview上运行这个javascript代码。
提前致谢。
答案 0 :(得分: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
,您传递的密钥范围在&#39;内。每次setValue
来电。
答案 1 :(得分:1)
db.setValue
是一种异步方法。因此,当您调用它时,它会与主程序流分离,同时保持对循环计数器i
的访问。 Javascript总是在执行任何异步代码之前完成当前流程。所以你的循环运行4次,每次创建一个异步代码块,只能在当前正在执行的程序(for循环)完成后运行。
请注意,每个异步块都可以访问相同的i
。因此,当他们执行时,他们所看到的只是在主流完成后存在的i的值(在这种情况下是4)。
考虑到这一点,处理这些问题的最简单方法是创建一个闭包。基本上,您将为每个异步块提供i的副本,该副本将保留在创建块时的值(除非您的异步块更改它)。你可以像@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);
}
}