我正在Google Chrome上编写在XAMPP上运行的本地应用程序。它与IndexedDB交互(我使用Jake Archibald的promise库)。这是我的问题。
让我们说我有一个具有2个属性的对象存储,日期和工资(那天赚钱)。我想console.log
整个对象商店让我们说一个报告。以下是代码段:
//Report 4 days of work
for(Day = 1; Day <= 4; Day++) {
dbPromise.then(function(db) {
var tx = transaction("workdays", "readonly");
return tx.objectStore("workdays").get(Day);
}).then(function(val) {
console.log(val.day + '\t' + val.salary + '$\n';
})
我的期望是这样的:
1 100$
2 120$
3 90$
4 105$
但它实际上是错误的,并说&#34;无法读取 undefined &#34;的价值日。结果是循环没有等待dbPromise.then()...
但是异步地继续,并且因为IndexDB请求很慢,所以当它们完成时,Day计数器已经是5并且没有匹配并返回记录。 / p>
我挣扎了一段时间然后找到了一个解决方法,在循环中放置一个DayTemp
来捕捉这一天。
//Report 4 days of work
for(Day = 1; Day <= 4; Day++) {
DayTemp = Day;
dbPromise.then(function(db) {
var tx = transaction("workdays", "readonly");
return tx.objectStore("workdays").get(DayTemp);
}).then(function(val) {
console.log(val.day + '\t' + val.salary + '$\n';
})
它运作良好。但那时它还没有。结果如下:
1 100$
4 105$
2 120$
3 90$
我需要它们。我需要做什么?非常感谢你!
注意:情况稍微复杂一些,所以我不能使用 getAll()
或游标等等。我真的要做循环。而且我也有兴趣在这个Javascript的同步/异步主题中受到启发。我是初学者。
更新:我想出来了!首先,我DateTemp
的第二次尝试的结果实际上是这样的:
4 105$
4 105$
4 105$
4 105$
这基本上反映了一开始的同样问题。这次唯一的区别是Date
被捕获,所以它没有超过4。
但我终于找到了所有这一切的解决方案。让我演示这个简单的Javascript片段:(1)
function dummy(day)
{
dbPromise.then(function(db) {
//Create transaction, chooose object stores, etc,..
objectStore.get(Day);
}).then(function(db) {
console.log(day + '\t' + salary + '\$n');
})
}
for(Day = 1; Day <= 4; Day++)
{
dummy(Day);
}
如果我像上面那样编写代码,我将永远得到正确答案!
1 100$
2 120$
3 90$
4 105$
但是,如果我这样写:(2)
for(Day = 1; Day <= 4; Day++)
{
dbPromise.then(function(db) {
//Create transaction, chooose object stores, etc,..
return objectStore.get(Day);
}).then(function(val) {
console.log(Day + '\t' + val.salary + '\$n');
})
}
我总是会收到一个错误,因为在数据库请求完成创建事务时Day
计数器已经是5,并输入使用get()
计数器的Day
方法!疯了吧?
似乎Javascript有一些东西来确定是否应该等待一个语句,这意味着它应该在其后面的其他语句开始执行之前完全执行。以(1)代码片段为例。在循环体内输入Day = 1
的循环,Javascript看到dummy()
作为参数传递Day
,并决定&#34;我不会继续循环没有让dummy()
先完成,否则他会把自己搞砸了#34;。我得到了一个漂亮的结果。
然而,在(2)代码段中。 Javascript进入循环,看到我打电话给then()
dbPromise
,它说&#34; Okk正在执行大男孩,但我没有看到任何理由我必须等你。你在Day
内使用then()
哈?我不在乎这件事,只看着对不起。在您提出要求时,我会增加Day
!&#34;。事情变得混乱。
这是我的第一点。我的第二点是,我还发现indexedDB中的add()
个请求是异步的,即使每个add()
都在不同的事务中。但get()
是同步的,所以很好。
现在希望您对我上面的更新答案提出意见。很确定他们在一些尴尬的方面是不正确的。必须有一些非常基本的东西,我在这里错过了Javascript。
答案 0 :(得分:1)
我建议您了解以下主题:
作为一般规则,永远不要在循环中定义函数。如果你没有在循环中定义一个函数,那么你将避免大部分的复杂性。
如果你坚持在循环中定义一个函数,你可以使用使用立即执行的函数表达式的技巧:
for(...) {
(function defined_plus_call(a, b, c) {
// operate on a and b and c here within this function's body
// do NOT use the variables arg1ToUseForA, arg2ToUseForB, etc
}(arg1ToUseForA, arg2ToUseForB, etc));
}
或者,如果您只为现代浏览器编写,并且已经熟悉promises和异步编程,那么您可以使用新的async / await语法,以便编写命令式循环:
function get_helper_function(tx, Day) {
function executor(resolve, reject) {
var request = tx.objectStore("workdays").get(Day);
request.onsuccess = function() { resolve(request.result); };
request.onerror = function() { reject(request.error); };
}
return new Promise(executor);
}
async function foo(...) {
for(Day = 1; Day <= 4; Day++) {
var promise_result = await dbPromise;
var tx = promise_result.transaction("workdays", "readonly");
var val = await get_helper_function(tx, Day);
console.log(val.day + '\t' + val.salary + '$\n';
}
}
然后,如果你真的想写清洁代码,我会改变一些其他的东西。一,这些都是可以共享相同数据库连接和相同事务的基本读取。第二,你可以使用Promise.all来迭代几个承诺。
function get_by_day(tx, day) {
function executor(resolve, reject) {
var request = tx.objectStore("workdays").get(day);
request.onsuccess = function() { resolve(request.result); };
request.onerror = function() { reject(request.error); };
}
return new Promise(executor);
}
function get_all(db) {
var db = await dbPromise;
var tx = db.transaction("workdays", "readonly");
var promises = [];
for(var day of days) {
var promise = get_by_day(tx, day);
promises.push(promise);
}
var all_promise = Promise.all(promises);
return all_promise);
}
get_all().then(function(resolutions) {
for(var val of resolutions) {
console.log(val.day + '\t' + val.salary + '$\n';
}
});
这样,get_all可以同时解析任何顺序的个人获取承诺,至少在理论上是这样。
然后,如果你想尝试优化,那就更进一步了,看看函数IDBObjectStore.prototype.getAll。而不是明确地获取每一天,在一个函数调用中加载一系列天。