[英]Nested multilayered async/await doesn't seem to wait
I have a piece of code simplified version of which looks like this:我有一段代码的简化版本如下所示:
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
})();
As you can see, self-executing, pseudo-recursive main()
calls for await fetchData()
, then fetchData()
calls for await tryFetch()
and finally tryFetch()
calls for await api.fetch('~')
, as it's defined in the api.如您所见,自执行的伪递归
main()
调用await fetchData()
,然后fetchData()
调用await tryFetch()
,最后tryFetch()
调用await api.fetch('~')
,如它在 api 中定义。
However, once I started the script and paused it after a couple of iterations, I noticed that both dataStorage1
and dataStorage2
remain undefined
.但是,一旦我启动脚本并在几次迭代后暂停它,我注意到
dataStorage1
和dataStorage2
都保持undefined
。 If I go through the code step by step in debugger, what happens is that the execution starts at the beginning of fetchData()
, moves to the await tryFetch();
如果我 go 在调试器中逐步通过代码,会发生什么情况是执行从
fetchData()
的开头开始,移动到await tryFetch();
line, skips it, and then goes onto the next iteration.行,跳过它,然后进入下一个迭代。
For the reference, if I call dataStorage1/2 = await api.fetch(`~`);
作为参考,如果我调用
dataStorage1/2 = await api.fetch(`~`);
in the body of main()
directly without any nesting, it works perfectly (unless error occurs, since they are not handled properly).在没有任何嵌套的情况下直接在
main()
的主体中,它可以完美地工作(除非发生错误,因为它们没有得到正确处理)。
So, my question is what have I missed?所以,我的问题是我错过了什么?
I think the problem is in this line: setTimeout(async () => await tryFetch(), delay);
我认为问题出在这一行:
setTimeout(async () => await tryFetch(), delay);
. . The
await
statement inside the callback makes the promise returned by that callback wait, not the whole function. So async () => await tryFetch()
is a function that returns a promise, but nothing waits for that promise to complete.回调中的
await
语句使回调等待返回 promise,而不是整个 function。因此async () => await tryFetch()
是一个返回 promise 的 function,但没有等待 promise 完成。
Try replacing that code with something line尝试用某行替换该代码
await new Promise((resolve) => setTimeout(resolve, delay));
await tryFetch();
Indeed, if in an async
function you call setTimeout
you cannot expect it to perform an await
on anything that relates to the callback passed to setTimeout
.实际上,如果在
async
function 中调用setTimeout
,则不能期望它对与传递给setTimeout
的回调相关的任何事情执行await
。 The call to setTimeout
returns immediately, and your while
loop is effectively a synchronous loop.对
setTimeout
的调用立即返回,您的while
循环实际上是一个同步循环。 It is a so called "busy loop" -- blocking your GUI as it potentially will loop for thousands of times.这是一个所谓的“繁忙循环”——阻塞您的 GUI,因为它可能会循环数千次。
As a rule of thumb, use setTimeout
only once : to define a delay
function, and then never again.根据经验,只使用
setTimeout
一次:定义delay
function,然后再也不会。
Also avoid using a global variable like stopLight
: this is bad practice.还要避免使用像
stopLight
这样的全局变量:这是不好的做法。 Let the async function return a promise that resolves when this is supposed to be true, and rejects when not.让 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.