#javascript #node.js #asynchronous #puppeteer
#javascript #node.js #асинхронный #кукловод
Вопрос:
async function getContent(){
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const content = await page.content();
await console.log(content);
await browser.close();
}
setInterval(searchTarget(), 13000);
У меня есть эта асинхронная функция, которая получает содержимое с веб-страницы с помощью puppeteer. Я хочу иметь возможность проверять веб-страницу так часто, поэтому я пытался использовать setinterval, но продолжал получать ошибки. Есть идеи?
Комментарии:
1. «Я продолжал получать ошибки» — какие ошибки ?
2. Мой совет: не используйте setInterval. Вызовите функцию снова в разрешении с таймаутом
3. Вы опубликовали вызываемую функцию
getContent
, но передаете результат вызова функции (а не саму функцию) с именемsearchTarget
setInterval
.4. Также вам не нужно иметь () в вызове интервала или тайм-аута
setInterval(searchTarget, 13000);
Ответ №1:
- Вы опубликовали определение
getContent
, а неsearchTarget
. - Вы передаете результат
searchTarget
вsetInterval
, а не передаете ссылку на функциюsearchTarget
. - Вы говорите, что получаете ошибки, но не сказали, что это за ошибки.
- Вы можете использовать
async function
withsetInterval
. - Вам не нужно использовать
await
withconsole.log
(вы не должны использоватьawait
, потому что asconsole.log
ничего не возвращает, не говоря уже о aPromise<T>
).
Я предполагаю, что вы на самом деле хотите этого:
async function getContent() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const content = await page.content();
console.log(content);
await browser.close();
}
const timerId = window.setInterval( getContent, 13 * 1000 );
// You can call `window.clearInterval( timerId )` to stop the interval loop.
Я рекомендую добавить обработку ошибок, которая останавливает цикл интервалов при первой ошибке:
/** @type {number} */
let intervalTimerId = null;
async function getContent() {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const content = await page.content();
await console.log(content);
await browser.close();
}
catch( err ) {
console.error( "Error in getContent: %o", err );
if( intervalTimerId ) {
window.clearInterval( intervalTimerId );
}
}
}
intervalTimerId = window.setInterval( getContent, 13 * 1000 );
Альтернативный подход:
Как отметили другие пользователи, такие как @mplungjan и @RohanAsokan, в вашем коде есть потенциальные проблемы с дизайном, потому setInterval
что он будет вызываться getContent
каждые 13 секунд, даже если предыдущий вызов getContent
еще не завершен — это может произойти, если (например) await page.content()
выполнение заняло больше 13 секунд.
В этом случае решение состоит в том, чтобы использовать window.setTimeout
вместо window.setInterval
и вызывать его изнутри getContent
, например:
/** @type {number} */
let lastSetTimeoutId = null;
async function getContentAndStartLoop() {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const content = await page.content();
console.log(content);
await browser.close();
lastSetTimeoutId = window.setTimeout( getContentAndStartLoop, 13 * 1000 );
}
catch( err ) {
console.error( "Error in getContent: %o", err );
}
}
Обратите внимание, что getContentAndStartLoop
это разрешится после первого цикла, но будет продолжаться до тех пор, пока не будет выдана ошибка.
Лучший подход:
Я думаю, было бы лучше структурировать ее следующим образом (используя Promise
-adapter для setTimeout
named delay
):
async function myProgram() {
const url = "https://thispersondoesnotexist.com/":
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
while( true ) {
await page.goto( url );
const content = await page.content();
console.log( content );
await delay( 13 * 1000 );
}
}
catch( err ) {
console.error( "Error in myProgram: %o", err );
}
finally {
await browser.close();
}
}
async function delay( ms, state = null ) {
return new Promise( ( resolve, reject ) => {
window.setTimeout( () => resolve( state ), ms );
} );
}
Ответ №2:
Я задам вам вопрос, и вы получите свой ответ.
Как вы думаете, что произойдет, если async
запрос на getContent()
займет больше времени, чем 13000
интервал в миллисекунду?
Следовательно, лучшим вариантом было бы разрешить обещание и снова вызвать функцию, как предложил @mplungjan в комментариях.
Возможным, но нечистым решением было бы очистить интервал, если предыдущее обещание не было выполнено.
Комментарии:
1. как бы мне решить обещание и вызвать функцию? (Я новичок в promises)
2. @b0b75 Вам не нужно вручную «разрешать» какие-либо обещания — это делает за вас
await
оператор. Смотрите Мой обновленный ответ для примера.3. @Dai Я не могу прокомментировать ваш пост. Но просто для того, чтобы указать, что, многократно вызывая функцию и устанавливая ее в бесконечный стек вызовов, вы теряете преимущества оптимизации, предоставляемые
setInterval
. @b0b75 тоже займись этим.4. @RohanAsokan «путем повторного вызова функции и установки ее в бесконечный стек вызовов» — пожалуйста, объясните, что вы подразумеваете под этим. Я не верю, что в опубликованном мной коде есть вероятность переполнения стека. Если есть проблема с моим кодом, пожалуйста, оставьте комментарий к моему ответу с более подробной информацией.
5. @rohanAsokan Не делает
setTimeout
и не используетPromise
. В любом случае, беспокоиться о размере стека в JS почти совершенно не нужно — либо ваш код вызывает бесконечную рекурсию и переполнение стека, либо нет. Помните, что, поскольку JS не имеет явного выделения стека (в отличие от C), единственный способ переполнения скрипта без ошибок заключался в том, что структура данных, которую он просматривал, была смехотворно огромной; чего не произойдет на практике (потому что, если бы он был таким большим, вы бы вообще не использовали JS)