如何使用异步函数与setInterval。

5
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,但是一直收到错误。有任何想法吗?

2
我一直收到错误信息 - 是什么错误 - Dai
1
我的建议是:不要使用setInterval。在resolve中使用timeout再次调用函数。 - mplungjan
1
你发布了一个名为 getContent 的函数,但是你正在将一个名为 searchTarget 的函数调用结果(而不是函数本身)传递给 setInterval - Dai
1
同时,您需要在间隔或超时调用setInterval(searchTarget, 13000);中不使用()。 - mplungjan
2个回答

5
  • 你已经发布了getContent的定义,而不是searchTarget的定义。
  • 你正在将searchTarget的结果传递给setInterval,而不是传递对函数searchTarget的引用。
  • 你说你收到了错误信息,但没有说明这些错误是什么。
  • 你可以使用async functionsetInterval一起使用。
  • 在使用console.log时不需要使用await(因为console.log不返回任何东西,更不用说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会每13秒调用一次getContent,即使前一个对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函数将在第一次循环后解决,但会继续运行直到抛出错误。
更好的方法是:
我认为最好这样结构化(使用一个名为delayPromise-适配器来代替setTimeout):
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
我该如何解决 Promise 并调用函数?(我对 Promise 完全不熟悉) - b0b75
@b0b75 你不需要手动“解决”任何 Promise - 这由 await 操作符为您处理。请参阅我的更新答案以获取示例。 - Dai
@RohanAsokan “通过反复调用函数并将其设置在无限调用堆栈中” - 请解释您的意思。我不认为我发布的代码存在堆栈溢出的可能性。如果我的代码有问题,请在我的答案下发表评论回复并提供更多细节。 - Dai
@RohanAsokan,另外,您所指的“优化优势”是什么? - Dai
1
@rohanAsokan setTimeout和使用Promise都不会。无论如何,在JS中担心堆栈大小几乎是完全不必要的 - 要么您的代码会导致无限递归和堆栈溢出,要么不会。请记住,因为JS没有显式的堆栈分配(不像C),所以非错误脚本溢出的唯一方法是它正在遍历的数据结构非常巨大; 在实践中不会发生这种情况(因为如果它那么大,您首先就不会使用JS)。 - Dai
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接