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());
}
});
});
}
Use the following code:
setInterval(async () => {
await fetch("https://jsonplaceholder.typicode.com/todos")
}, 100);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With