如何在JS的setInterval内部等待?

时间:2018-08-13 20:37:30

标签: javascript asynchronous promise async-await 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 = 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());
        }
    });
});
}

extractDate方法的格式如下:

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

现在的问题是,我的代码一直在滚动,但是它不等待setInterval中的其他内容完成,因为它每2秒滚动一次,但是通常extractDate函数应该花费的时间超过2秒,所以我实际上想等待setInterval中的所有内容完成之后再调用新间隔。

由于东西的异步性质,我没有设法console.log东西,所以请查看代码的行为。

那么,如何在下一次间隔调用之前确保setInterval中的所有内容都已完成?

编辑:

使用setTimeout的此解决方案仅滚动一次,并向操纵up发送未处理的承诺拒绝错误。

 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());
            }
        });
    });
}

6 个答案:

答案 0 :(得分:4)

使用以下代码:

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

答案 1 :(得分:2)

我通常选择此解决方案。我认为它更干净:

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)
  }
}

您的loop函数将返回一个承诺。您可以等待它停止循环,也可以丢弃它。

答案 2 :(得分:1)

将interval函数转换为递归setTimeout函数,这样,一旦函数完成,您就可以为下一次迭代初始化超时。

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);

答案 3 :(得分:1)

将时间间隔改为一个函数,并使用setTimeout将以后的函数调用排队。

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

然后使用setTimeout函数在将来的队列中排队:

setTimeout(interval, 2000);

小提琴示例:http://jsfiddle.net/t9apy3ec/5/

答案 4 :(得分:0)

如果某人想要更新的解决方案,则可以使用react-timeout,当卸载包装的组件时,该超时会自动取消所有延迟计时器。

更多信息

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

npm i react-timeout

使用await时,您可以执行以下操作

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

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

答案 5 :(得分:0)

在将异步函数用作setIntervalsetTimeout的参数时,请确保将所有可能引发错误的操作包装在try / catch中,以避免未处理的错误,以及在Node上。 js,请勿调用process.exit,因为它会抢占事件循环,而无需考虑计时器。在Async, Promises, and Timers in Node.js中有更多详细信息。