[英]How to force synchronous loop execution in Javascript?
我正在編寫在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
放入循環中以捕獲這樣的Day。
//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
使用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
,並決定“在沒有讓dummy()
首先完成的情況下,我無法繼續循環,否則他會會搞砸自己”。 我得到一個美麗的結果。
但是,在(2)代碼段中。 Javascript進入循環,看到我調用了dbPromise
then()
,它說:“好吧,執行大個子,但是我看不到有什么理由要等你。哦,你在then()
使用Day
嗎?不用擔心,只想引起您的歉意。在您處理請求時,我將增加Day
!”。 事情變得混亂了。
這是我的第一點。 我的第二點是,我還發現indexedDB中的add()
請求是異步的,即使每個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可以以任何順序同時解決個人獲得承諾。
然后,如果要嘗試進行優化,請走得更遠,然后查看功能IDBObjectStore.prototype.getAll。 無需顯式獲取每天的時間,而是在一個函數調用中加載幾天的時間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.