递归函数无法从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
相关产品推荐
相关产品推荐

