如何在Node.js中每30秒执行调用axios的函数并解决内存溢出问题
问题原因说明
- 你写的同步
while(true)死循环会直接阻塞Node.js的单线程事件循环,所有异步请求、定时器操作都无法被调度,同时循环会不断生成调用栈占用内存,最终触发堆内存溢出。 - 之前用
setInterval出问题的原因是:它不会等待上一次mvcAppointmentSearch内部的所有异步请求处理完成,每30秒就强制触发新的执行,导致请求堆积、cheerio加载的DOM对象无法被垃圾回收,内存占用持续上涨最终溢出。
可行的解决方案
我们用异步循环+等待上轮执行完成再间隔的逻辑实现,既保证每轮请求都处理完成再等30秒执行下一轮,也不会出现请求堆积问题:
1. 先修改原有异步函数的逻辑,确保可以等待执行完成
const express = require('express'); const request = require('request-promise'); const cheerio = require('cheerio'); const axios = require('axios'); const cssSelect = require('css-select'); const open = require('open'); const mvcUrl = 'https://telegov.njportal.com/njmvc/AppointmentWizard/14/'; const mvcLocation = ['Edison', 'Rahway', 'SouthPlainfield']; const mvcLocationNumber = ['163']; const requiredMonths = ['September', 'October']; // 原callUrl保持async,天然返回Promise const callUrl = async (url, locationNumberIndex) => { try { const response = await axios.get(url); getData(response.data, locationNumberIndex); } catch (err) { console.log(err); } }; // 修改mvcAppointmentSearch为async,等待所有地址请求完成 const mvcAppointmentSearch = async () => { // 收集所有请求的Promise,用Promise.all等待全部执行完成 const requestTasks = []; for (let i = 0; i < mvcLocationNumber.length; i++) { const currentUrl = mvcUrl + mvcLocationNumber[i]; console.log(mvcLocationNumber[i]); requestTasks.push(callUrl(currentUrl, i)); } await Promise.all(requestTasks); }; const getData = (html, locationNumberIndex) => { let data = []; let $ = cheerio.load(html); $.prototype.exists = function (selector) { return this.find(selector).length > 0; }; const checkerLength = $('div').exists('.alert-danger'); console.log(checkerLength); if (checkerLength) { console.log( `No appointment available in ${mvcLocation[locationNumberIndex]}` ); } else { const dateString = $('.control-label').text(); const availableMonth = dateString.trim().split(' ')[7]; const exactDateAvailability = dateString.slice(24, -1); console.log(availableMonth); if (requiredMonths.includes(availableMonth)) { console.log('Hurray there is an appointment available'); const message = `Appointment available for the location ${mvcLocation[locationNumberIndex]} on ${exactDateAvailability}`; open(`${mvcUrl + mvcLocationNumber[locationNumberIndex]}`); console.log(message); } else { console.log('required Month is not available still searching'); } } };
2. 替换原有同步死循环,实现异步间隔执行
// 封装sleep工具函数,返回指定毫秒后resolve的Promise const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // 异步循环函数 const startLoop = async () => { while (true) { try { console.log('开始执行本轮查询'); // 等待本轮所有查询执行完成 await mvcAppointmentSearch(); console.log('本轮查询完成,等待30秒后执行下一轮'); // 等待30秒 await sleep(30 * 1000); } catch (err) { console.log(`执行出错:${err}`); // 出错也等30秒再重试,避免出错后高频循环 await sleep(30 * 1000); } } } // 启动循环 startLoop();
优化说明
该写法保证上一轮所有请求、页面解析逻辑全部处理完成后,才会开始计时30秒执行下一轮,不会出现请求堆积,从根源上避免了堆内存溢出的问题。
内容的提问来源于stack exchange,提问作者Sumit Ghewade
相关产品推荐
相关产品推荐

