中止在调用 puppeteer page.evaluate() 时运行的 Promise

问题描述 投票:0回答:1

在 puppeteer 中,我可以使用中止信号来中止各种操作。例如,我可以中止 HTTP 请求

.waitForSelector()

我意识到

page.evaluate()
返回一个值,而不是Promise。我不需要中止
page.evaluate()
。我希望能够根据代码其余部分中发生的情况来中止
page.evaluate()
内的承诺。例如,在本例中,我想将所有这些事件相互竞争:一个
.waitForSelector
、一个
.waitForNavigation
以及我正在浏览器页面上侦听的 jQuery 事件。我还需要删除事件侦听器,但我希望在中止时可以做到这一点。

超时在这里不够好,因为它不会让我的程序在需要时适当退出,并且它不会正确地让我的测试套件在需要时继续进行下一个测试。据我所知,try/catch 等待的时间同样长。

javascript promise puppeteer signals abort
1个回答
0
投票

我想出的解决方案使用放在

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
,因为它的控制台日志在其他更晚的控制台日志中间触发。如果承诺尚未接受中止,那么一旦做出承诺,您就无法中止它,所以我不确定如何处理。

就像我在评论中所说的那样,我似乎无法将点击承诺放入比赛中。当我尝试我的代码时超时。

不过,我的实际代码更复杂,因此我需要使用更简单的示例来进行故障排除。

© www.soinside.com 2019 - 2024. All rights reserved.