简体   繁体   English

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

[英]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 .但是,一旦我启动脚本并在几次迭代后暂停它,我注意到dataStorage1dataStorage2都保持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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM