在 puppeteer 中,我可以使用中止信号来中止各种操作。例如,我可以中止 HTTP 请求 和
.waitForSelector()
。
我意识到
page.evaluate()
返回一个值,而不是Promise。我不需要中止page.evaluate()
。我希望能够根据代码其余部分中发生的情况来中止 page.evaluate()
内的承诺。例如,在本例中,我想将所有这些事件相互竞争:一个 .waitForSelector
、一个 .waitForNavigation
以及我正在浏览器页面上侦听的 jQuery 事件。我还需要删除事件侦听器,但我希望在中止时可以做到这一点。
超时在这里不够好,因为它不会让我的程序在需要时适当退出,并且它不会正确地让我的测试套件在需要时继续进行下一个测试。据我所知,try/catch 等待的时间同样长。
我想出的解决方案使用放在
window
上的变量在浏览器中设置了一个可中止的承诺。稍后,当我需要清理时,我使用这些窗口变量来中止承诺并删除事件侦听器。
重要的一件事是,常规节点(
page.evaluate
之外)中发生的事情与page.evaluate
中发生的事情完全分开。有一个名为 waitForFunction
的木偶操作方法,它可能有帮助,也可能没有帮助,但我还不知道如何使用它。
这就是为什么我为
page.evaluate
之外的等待创建一个不同的中止控制器。
我还没有解决所有问题。我在评论里解释过。
function getEventPromise() {
/** Wait for an event on the browser. */
const jQueryEventPromise = page.evaluate(() => {
// add window variables to use later
// the abort controller for the browser
const controller = new AbortController()
const signal = controller.signal
// abortable promise that adds a one-time event listener
return new Promise((resolve, reject) => {
// aborting in here will clean up the page.evaluate too
// if the signal is already aborted, just throw an error
if (signal.aborted) {
reject(signal.reason)
}
// prepare to throw an error later if the browser code aborts
signal.addEventListener("abort", () => {
reject(signal.reason)
})
// otherwise, this handle should get called and resolve the promise
function handler(event) {
resolve("the event happened")
}
$(document).on("event", handler)
})
.then((returnedValue) => {console.log(returnedValue)})
.catch((error) => {console.log(error.name)})
})
return jQueryEventPromise
}
// the abort controller for node
const controller = new AbortController()
const signal = controller.signal
// prepare the promises - they'll be waiting to detect what happens
const elementPromise = page.waitForSelector("#the-id", {visible: true, signal})
// other promises
// I haven't solved how to use waitForNavigation in here. I think it doesn't use an abort signal.
// do something, like clicking an element
const clickPromise = element.click()
// When I include clickPromise my race times out. I'm not sure why yet.
await Promise.race([
Promise.all([/*clickPromise,*/ elementPromise]),
Promise.all([/*clickPromise,*/ getEventPromise()])
])
// cancel the things that didn't happen
// abort the node promises
controller.abort()
// remove the event listener and abort the browser promise
await page.evaluate(() => {
$(document).off("event")
controller.abort()
})
// do other things
在实际代码中,您需要更加小心在窗口上放置的变量,因为如果您同时多次执行此操作,则可能会覆盖这些变量。你想要像
window[uniqueName].controller = new AbortController()
这样的东西。
您还希望在 jQuery 事件中使用它来为事件指定一个类别,例如 jQuery 文档 上的示例中的“click.validator”。也就是说,
$(document).off("event")
将是 $(document).off(`event.${uniqueName}`)
,而 $(document).on("event", handler)
将是 $(document).on(`event.${uniqueName}`, handler)
。也许还有另一种方法来处理该部分,但我还没有找到一种方法来仅传递事件和处理程序而不传递选择器,这是我不想要的。
我的实际代码中还有其他问题。
就像我在评论中所说的那样,
waitForNavigation
似乎可能不会使用signal
,因为它的控制台日志在其他更晚的控制台日志中间触发。如果承诺尚未接受中止,那么一旦做出承诺,您就无法中止它,所以我不确定如何处理。
就像我在评论中所说的那样,我似乎无法将点击承诺放入比赛中。当我尝试我的代码时超时。
不过,我的实际代码更复杂,因此我需要使用更简单的示例来进行故障排除。