簡體   English   中英

嵌套多層 async/await 好像不等待

[英]Nested multilayered async/await doesn't seem to wait

我有一段代碼的簡化版本如下所示:

let dataStorage1; //declare global vars for easier access later on
let dataStorage2;
let stopLight = true; //this variable is used to 'mark' an iteration as successful (= true) or
//failed (= false) and in need of a retry before continuing to the next 
//iteration
let delay = 2000; //the standard time for a delay between api calls

async function tryFetch() {
  try {
    dataStorage1 = await api.fetch('data_type_1'); //fetch needed data trough api, which
    //fills the global variable with an 
    //object
    dataStorage2 = await api.fetch('data_type_2'); //do the same
    stopLight = true; //change the value of stopLight to true, thus marking this iteration
    //as successful
  } catch (err) {
    console.log(err);
    stopLight = false;
  }
}

async function fetchData() {
  stopLight = true; //change the stopLight to default before execution

  await tryFetch(); //fetch data and assign it to variables

  //this section is needed for retrial of fetching after a 2s delay if the first attempt was
  //unsuccessful, which is repeated until it's either successful or critical error occurred
  while (stopLight == false) {
    setTimeout(async () => await tryFetch(), delay);
  }
}

(async function main() {
  await fetchData(); //finally call the function
  setTimeout(main, delay); //repeat the main function after 2s
})();

如您所見,自執行的偽遞歸main()調用await fetchData() ,然后fetchData()調用await tryFetch() ,最后tryFetch()調用await api.fetch('~') ,如它在 api 中定義。

但是,一旦我啟動腳本並在幾次迭代后暫停它,我注意到dataStorage1dataStorage2都保持undefined 如果我 go 在調試器中逐步通過代碼,會發生什么情況是執行從fetchData()的開頭開始,移動到await tryFetch(); 行,跳過它,然后進入下一個迭代。

作為參考,如果我調用dataStorage1/2 = await api.fetch(`~`); 在沒有任何嵌套的情況下直接在main()的主體中,它可以完美地工作(除非發生錯誤,因為它們沒有得到正確處理)。

所以,我的問題是我錯過了什么?

我認為問題出在這一行: setTimeout(async () => await tryFetch(), delay); . 回調中的await語句使回調等待返回 promise,而不是整個 function。因此async () => await tryFetch()是一個返回 promise 的 function,但沒有等待 promise 完成。

嘗試用某行替換該代碼

await new Promise((resolve) => setTimeout(resolve, delay));
await tryFetch();

實際上,如果在async function 中調用setTimeout ,則不能期望它對與傳遞給setTimeout的回調相關的任何事情執行await setTimeout的調用立即返回,您的while循環實際上是一個同步循環。 這是一個所謂的“繁忙循環”——阻塞您的 GUI,因為它可能會循環數千次。

根據經驗,只使用setTimeout一次:定義delay function,然后再也不會。

還要避免使用像stopLight這樣的全局變量:這是不好的做法。 讓 async function 返回一個 promise,它在應該為真時解析,在不為真時拒絕

// Utility function: the only place to use setTimeout
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function tryFetch() {
    try {
        let dataStorage1 = await api.fetch('data_type_1'); 
        let dataStorage2 = await api.fetch('data_type_2'); 
        return { dataStorage1, dataStorage2 }; // use the resolution value to pass results
    } catch (err) {
        console.log(err);
        // retry
        throw err; // cascade the error!
    }
}

async function fetchData() {
    while (true) {
        try { 
            return await tryFetch(); // fetch data and return it
        } catch (err) {} // repeat loop
    }
}

(async function main() {
    let intervalTime = 2000; //the standard time for a delay between api calls

    while (true) { // for ever
        let { dataStorage1, dataStorage2 } = await fetchData();
        // ... any other logic that uses dataStorage1, dataStorage2
        //     should continue here...
        await delay(intervalTime); //repeat the main function after 2s
    }
})();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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