Как использовать асинхронную функцию с заданным интервалом

#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 with setInterval .
  • Вам не нужно использовать await with console.log (вы не должны использовать await , потому что as console.log ничего не возвращает, не говоря уже о a Promise<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)