You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归函数无法从Promise触发调用?移除Nightmare的end()有何影响?

Nightmare.js递归调用仅执行一次,移除.end()后恢复正常,求解释.end()的影响

问题描述

我尝试了各种方法,但递归函数始终只能被调用一次,无法实现递归执行。以下是我的代码:

var scrape = () => { 
  var waitTime = randomIntFromInterval(1000, 10000); 
  nightmare.goto('http://...') 
    .wait(waitTime) 
    .evaluate(() => { 
      var data = []; 
      // ... doing work to get data from browser here 
      return data 
    }) 
    .end() 
    .then((result) => { 
      console.log('scraped successfully!' + new Date()); 
      scrape(); 
    }) 
} 
scrape();

我原本认为Promise的最终.then()意味着Promise已完成且执行成功,但递归并未生效。更新:移除.end()后问题得以解决,我未完全理解Nightmare文档中关于.end()完成队列操作的描述,该代码参考自示例,想了解移除.end()会产生哪些影响?


解答

先搞懂.end()到底干了啥

Nightmare里的.end()可不是单纯的“结束当前队列”——它的核心作用是终止整个Nightmare实例,关闭背后的Electron浏览器进程,然后返回一个Promise,这个Promise会在浏览器彻底关闭后才resolve。

你原来的代码里,第一次调用scrape()时,.end()直接把nightmare实例干掉了。等.then()触发想再次调用scrape()时,那个nightmare实例已经凉透了,根本没法再执行.goto()这类需要浏览器进程的操作,所以递归直接断了,只跑了第一次。

移除.end()的影响

移除.end()后,你的nightmare实例会一直保持活跃,每次递归都复用同一个浏览器进程,这就是递归能正常工作的原因。但这里要注意几个关键影响:

  • 资源占用问题:如果递归要跑很多次,一直开着浏览器会持续占内存,跑久了可能出现内存泄漏或者性能下滑。要是长期运行的爬虫,得记得在爬完所有目标后手动调用nightmare.end()释放资源,或者定期重启实例。
  • 会话状态保留:复用同一个实例意味着浏览器的Cookie、本地存储这些会话数据会保留。如果爬的网站需要维持登录状态,这是好事;但要是每次爬取都需要全新的会话,那你要么每次递归创建新的Nightmare实例,要么在爬取前手动清理会话数据。
  • 为啥示例里用.end():你参考的示例应该是单次爬取的场景——爬完一次就收工,所以用.end()关闭浏览器很合理。但递归爬取是持续复用实例的场景,显然不能每次爬完就把浏览器关掉。

小优化建议

如果你的递归需要长期运行,可以这么调整:

  • 给递归加个终止条件(比如爬够100页、没有更多数据了),满足条件时手动调用nightmare.end()关闭浏览器,释放资源。
  • 要是担心单实例内存撑不住,可以每爬取N次就关闭当前实例,创建新的实例继续递归。

内容的提问来源于stack exchange,提问作者Taylor A. Leach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:54:42