javascript 如何在 JS 中的 setInterval 内等待?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/51830200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to await inside setInterval in JS?
提问by tinker
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 extractDatemethod has the following form:
其中extractDate方法具有以下形式:
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 setIntervalto finish, as it keeps scrolling every 2 seconds, but normally extractDatefunction should take longer than 2 seconds, so I actually want to await for everything inside setIntervalto finish before making the call to the new interval.
现在的问题是,我的代码不断滚动,但它不会等待里面的其他东西setInterval完成,因为它每 2 秒滚动一次,但通常extractDate功能应该需要超过 2 秒,所以我实际上想要等待setInterval在调用新的时间间隔之前,里面的所有东西都要完成。
Because of the async nature of stuff, I didn't manage to console.logstuff so see the behavior of the code.
由于东西的异步性质,我没有设法console.log填充,所以请查看代码的行为。
So, how can I make sure that everything inside setIntervalfinishes before making the next interval call?
那么,setInterval在进行下一次间隔调用之前,如何确保里面的所有内容都完成了呢?
EDIT:
编辑:
This solution using setTimeoutscrolls just once and throws unhandled promise rejection error with puppeteer.
此解决方案setTimeout仅使用滚动一次,并使用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());
}
});
});
}
采纳答案by CertainPerformance
Turn the interval function into a recursive setTimeoutfunctioninstead, that way you can initialize a timeout for the next iteration once the function has finished.
将间隔函数改为递归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);
回答by Zeeshan Ahmad
Use the following code:
使用以下代码:
setInterval(async () => {
await fetch("https://www.google.com/")
}, 100);
回答by Gustav G
Make the interval a function instead and use setTimeout to queue the future function call.
将间隔改为函数并使用 setTimeout 将未来的函数调用排队。
const interval = async function () { // instead of setInterval
Then use setTimeout function where you want to to queue the future call:
然后使用 setTimeout 函数,将未来的调用放入队列中:
setTimeout(interval, 2000);
Fiddle example: http://jsfiddle.net/t9apy3ec/5/
小提琴示例:http: //jsfiddle.net/t9apy3ec/5/
回答by Will Brickner
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 loopfunction will return a promise. You can wait for it to stop looping, or you can discard it.
您的loop函数将返回一个承诺。您可以等待它停止循环,也可以丢弃它。
回答by Novice_JS
If someone wants an updated solution there is react-timeout which cancels any lingering timers automatically when wrapped component is unmounted.
如果有人想要更新的解决方案,则有 react-timeout 会在卸载包装的组件时自动取消任何延迟的计时器。
More info
更多信息
https://www.npmjs.com/package/react-timeout
npm i react-timeout
While using await you can do something like this
在使用 await 你可以做这样的事情
handleClick = async() => {
try {
await this.props.getListAction().then(()=>{
this.timeOut = this.props.setTimeOut(this.handleClick, 10000);
});
} catch(err) {
clearTimeout(this.timeOut);
}
}

