How to await inside setInterval in JS?

风流意气都作罢 提交于 2021-01-21 03:55:25

问题


I have a code segment that looks like this:

async function autoScroll(page, maxDate = null) {
  await page.evaluate(async () => {
    await new Promise(async (resolve, reject) => {
        try {
            const scrollHeight = document.body.scrollHeight;
            let lastScrollTop = 0;

            const interval = setInterval(async () => {
                window.scrollBy(0, scrollHeight);
                const scrollTop = document.documentElement.scrollTop;
                let lastDate = null;

                if (maxDate) {
                    const html = new XMLSerializer().serializeToString(document.doctype) + document.documentElement.outerHTML;

                    await extractDate(html).then((date) => {
                        lastDate = date;
                    });
                }

                if (scrollTop === lastScrollTop || 
                    (maxDate && lastDate && maxDate.getTime() >= lastDate.getTime())) {
                    clearInterval(interval);
                    resolve();
                } else {
                    lastScrollTop = scrollTop;
                }
            }, 2000);
        } catch (err) {
            console.error(err);
            reject(err.toString());
        }
    });
});
}

Where extractDate method has the following form:

function extractDate(html) {
    return new Promise((resolve, reject) => {
        // Rest removed for brevity.
        resolve(result);
    });
}

Now the problem is that, my code keeps scrolling, but it doesn't wait for the other stuff inside setInterval to finish, as it keeps scrolling every 2 seconds, but normally extractDate function should take longer than 2 seconds, so I actually want to await for everything inside setInterval to finish before making the call to the new interval.

Because of the async nature of stuff, I didn't manage to console.log stuff so see the behavior of the code.

So, how can I make sure that everything inside setInterval finishes before making the next interval call?

EDIT:

This solution using setTimeout scrolls just once and throws unhandled promise rejection error with puppeteer.

 async function autoScroll(page, maxDate = null) {
     await page.evaluate(async () => {
        await new Promise(async (resolve, reject) => {
            try {
               const scrollHeight = document.body.scrollHeight;
               let lastScrollTop = 0;

                const interval = async function() {
                    window.scrollBy(0, scrollHeight);
                    const scrollTop = document.documentElement.scrollTop;
                    let lastDate = null;

                    if (maxDate) {
                        const html = new XMLSerializer().serializeToString(document.doctype) + document.documentElement.outerHTML;
                        await extractDate(html).then((date) => {
                            lastDate = date;
                        });
                    }

                    if (scrollTop === lastScrollTop || 
                       (maxDate && lastDate && maxDate.getTime() >= lastDate.getTime())) {
                        resolve();
                    } else {
                        lastScrollTop = scrollTop;
                        setTimeout(interval, 2000);
                    }
                }

                setTimeout(interval, 2000);

            } catch (err) {
                console.error(err);
                reject(err.toString());
            }
        });
    });
}

回答1:


Turn the interval function into a recursive setTimeout function instead, that way you can initialize a timeout for the next iteration once the function has finished.

async function doScroll() {
  window.scrollBy(0, scrollHeight);
  const scrollTop = document.documentElement.scrollTop;
  let lastDate = null;
  if (maxDate) {
    const html = new XMLSerializer().serializeToString(document.doctype) + document.documentElement.outerHTML;
    await extractDate(html).then((date) => {
      lastDate = date;
    });
  }
  if (scrollTop === lastScrollTop ||
      (maxDate && lastDate && maxDate.getTime() >= lastDate.getTime())) {
    // No need to `clearInterval`:
    resolve();
  } else {
    lastScrollTop = scrollTop;
    // Recursive setTimeout:
    setTimeout(doScroll, 2000); // <------------------
  }
}
setTimeout(doScroll, 2000);



回答2:


Use the following code:

setInterval(async () => {
    await fetch("https://www.google.com/") 
}, 100);



回答3:


I generally opt for this solution. I think it's cleaner:

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function loop() {
  while (/* condition */) {
    /* code to wait on goes here (sync or async) */    

    await delay(100)
  }
}

Your loop function will return a promise. You can wait for it to stop looping, or you can discard it.




回答4:


Make the interval a function instead and use setTimeout to queue the future function call.

const interval = async function () { // instead of setInterval

Then use setTimeout function where you want to to queue the future call:

setTimeout(interval, 2000);

Fiddle example: http://jsfiddle.net/t9apy3ec/5/




回答5:


If someone wants an updated solution there is react-timeout which cancels any lingering timers automatically when wrapped component is unmounted.

More info

https://www.npmjs.com/package/react-timeout

npm i react-timeout

While using await you can do something like this

  handleClick = async() => {
    try {
      await this.props.getListAction().then(()=>{
        this.timeOut = this.props.setTimeOut(this.handleClick, 10000);
      });

    } catch(err) {
      clearTimeout(this.timeOut);
    }
  }



回答6:


When using an async function as an argument to setInterval or setTimeout, make sure you wrap any operations that can throw errors in a try/catch to avoid un-handled errors and, on Node.js, do not call process.exit as it will pre-empt the event loop without any respect for timers. More detail in Async, Promises, and Timers in Node.js.



来源:https://stackoverflow.com/questions/51830200/how-to-await-inside-setinterval-in-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!