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

如何在循环中停止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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:37:38