如何在循环中停止setTimeout并解决脚本无法重复执行问题
嘿,我来帮你解决这两个核心问题——setTimeout循环控制的问题,还有data.map is not a function的报错,咱们一步步拆解:
问题1:循环中
setTimeout的控制与脚本重复运行失效 你当前的代码用了全局变量cnt来追踪遍历进度,这就踩了个坑:当定时任务每分钟触发一次callIt时,第一次遍历完所有symbols后,cnt已经等于symbols.length了,第二次触发callIt时,symbols[cnt]会变成undefined,导致API请求错误,后续流程全乱。
另外,你尝试用clearTimeout(callIt)无效,是因为clearTimeout需要传入**setTimeout返回的定时器ID**,而不是函数本身。不过在你的场景里,其实不需要主动清除,只要保证每次遍历完成后不再递归调用setTimeout,并且每次定时任务启动时都重置遍历进度就行。
问题2:
TypeError: data.map is not a function报错 这个错误的本质是:API返回的data不是数组。大概率是因为cnt超出了symbols的长度,导致请求的API URL里symbol参数无效,API返回了错误格式的响应(比如一个错误对象),而非预期的K线数组。
完整修复方案
我把代码做了针对性修改,解决上述两个问题:
1. 重构callIt函数,去掉全局变量,用递传索引控制遍历
const callIt = (index = 0) => { // 遍历完成,终止递归 if (index >= symbols.length) { console.log('所有币种数据采集完成'); return; } fetch(`https://api.binance.com/api/v3/klines?symbol=${symbols[index]}&interval=30m&limit=1`) .then(res => { // 先检查API请求是否成功 if (!res.ok) { throw new Error(`API请求失败,状态码:${res.status}`); } return res.json(); }) .then(data => { // 强制检查数据格式,避免map报错 if (!Array.isArray(data)) { throw new Error(`币种${symbols[index]}返回的数据不是预期数组格式`); } const btcusdtdata = data.map(d => ({ Open: parseFloat(d[1]), High: parseFloat(d[2]), Low: parseFloat(d[3]), Close: parseFloat(d[4]), Volume: parseFloat(d[5]), Timespan: 30, })); console.log(`处理币种${symbols[index]}数据:`, btcusdtdata); // 把当前币种传入数据库函数,避免依赖全局变量 return saveToDatebase(btcusdtdata, symbols[index]); }) .then(() => { // 数据库保存成功后,延迟3秒处理下一个 setTimeout(() => callIt(index + 1), 3000); }) .catch((err) => { console.error(`处理币种${symbols[index]}时出错:`, err); // 出错后继续处理下一个,避免中断整个流程 setTimeout(() => callIt(index + 1), 3000); }) };
2. 重构saveToDatebase为Promise,增强流程控制
const saveToDatebase = function(BTCdata, symbol) { return new Promise((resolve, reject) => { const url = 'mongodb+srv://username:password@cluster0-1kunr.mongodb.net/<dbname>?retryWrites=true&w=majority'; const today = new Date(); const date = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`; const time = `${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`; const dateTime = `${date} ${time}`; MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, db) => { if (err) { db?.close(); return reject(err); } const dbo = db.db('CryptoCurrencies'); const myobj = { Name: symbol, Array: BTCdata, Date: dateTime }; dbo.collection(symbol).insertOne(myobj, (error, res) => { db.close(); if (error) return reject(error); console.log(`1条数据已插入${symbol}集合`); resolve(); }); }); }); };
3. 定时任务重置遍历进度
const j = schedule.scheduleJob('*/1 * * * *', () => { console.log('===== 开始新一轮数据采集 ====='); callIt(0); // 每次定时任务启动时,从第一个币种开始处理 });
修复后的优势
- 去掉全局
cnt,避免多次定时任务触发时的状态污染 - 增加API响应和数据格式校验,提前捕获错误,杜绝
map报错 - 数据库操作改为Promise,确保数据保存完成后再处理下一个币种
- 出错后自动跳过当前币种继续处理,不会中断整个采集流程
内容的提问来源于stack exchange,提问作者Hexycode
相关产品推荐
相关产品推荐

