[英]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 中定義。
但是,一旦我啟動腳本並在幾次迭代后暫停它,我注意到dataStorage1
和dataStorage2
都保持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.