簡體   English   中英

如何在Javascript中強制執行同步循環?

[英]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的非常基本的顯而易見的事情。

我建議您了解以下主題:

  • 異步編程
  • 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM