- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个在 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$
但它实际上给出了一个错误,说“无法读取未定义的值日”。结果循环没有等待 dbPromise.then()...
但异步继续,并且由于 IndexDB 请求很慢,当它们完成时,Day 计数器已经是 5,并且没有匹配和返回记录。
我挣扎了一段时间,然后通过输入 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$
这基本上反射(reflect)了一开始同样的问题。这次唯一的区别是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
以来,我总是会收到错误消息当数据库请求完成事务创建并输入 get()
时,计数器已经为 5。使用 Day
的方法柜台!疯了吧?
似乎 Javascript 有一些东西来确定是否应该等待一条语句,这意味着它应该在其后面的其他语句开始执行之前完全执行。以(1)代码片段为例。输入 Day = 1
进入循环,在循环体内,Javascript 看到 dummy()
已通过Day
作为参数,并决定“如果不让dummy()
先完成,我就不会继续循环,否则他会把自己搞砸的”。我得到了一个漂亮的结果。
但是在 (2) 代码片段中。 Javascript 进入循环,看到我调用了 then()
的dbPromise
上面写着“好吧,正在执行大男孩,但我看不出有什么理由必须等你。哦,你在 Day
里面使用 then()
哈?我不在乎这个,只是看看外面抱歉。”当您执行请求时,我将增加 Day
!”。事情变得一团糟。
这是我的第一点。我的第二点是,我还发现add()
indexedDB 中的请求是异步的,即使每个 add()
位于不同的事务中。但是get()
是同步的,所以没问题。
现在希望您对我上面的更新答案发表意见。很肯定他们在某些令人尴尬的方面是错误的。关于 Javascript,我肯定错过了一些非常基础的、显而易见的事情。
最佳答案
我建议您了解以下主题:
作为一般规则,切勿在循环内定义函数。如果不在循环中定义函数,那么您将避免大部分复杂性。
如果您坚持在循环中定义函数,则可以使用立即执行函数表达式的技巧:
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));
}
或者,如果您只为现代浏览器编写代码,并且已经熟悉 Promise 和异步编程,那么您可以使用新的 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 迭代多个 Promise。
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 可以以任意顺序同时解析各个 get promise ,至少在理论上是这样。
然后,如果您想尝试优化,请更进一步,查看函数 IDBObjectStore.prototype.getAll。不要显式获取每一天,而是在一个函数调用中加载一系列日期。
关于javascript - 如何在 Javascript 中强制同步循环执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46067704/
我是一名优秀的程序员,十分优秀!