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

添加setTimeout后data.map函数二次调用失效问题排查

解决定时API调用时的TypeError: data.map is not a function问题

嘿,我一眼就定位到你这个问题的核心了——全局变量cnt在第一次脚本执行完毕后没有被重置!

咱们来捋一遍:第一次运行callIt时,cnt从0开始递增,直到等于symbols的长度(也就是6),这时候所有符号都处理完了。但当每小时的定时任务再次触发callIt时,cnt还是6,这时候symbols[cnt]就是undefined,你请求的API地址会变成https://api.binance.com/api/v3/klines?symbol=undefined&interval=30m&limit=1。Binance的API遇到这种无效参数,返回的不是你预期的K线数组,而是一个错误对象,自然就没法调用.map()方法了,这就是你看到那个报错的原因。

修复方案

我给你调整了代码,不仅解决了cnt重置的问题,还优化了几个细节让脚本更健壮:

const { MongoClient } = require('mongodb');
const schedule = require('node-schedule');
const fetch = require("node-fetch");

const symbols = ["ZRXBTC", "LENDBTC", "AEBTC", "AIONBTC", "ALGOBTC", "ARDRBTC"];
// 复用MongoDB连接,避免每次插入都重新建立连接
let mongoClient;

// 初始化MongoDB连接(只执行一次)
async function initMongoConnection() {
  const url = 'mongodb+srv://username:password3@cluster0-1kunr.mongodb.net/<dbname>?retryWrites=true&w=majority';
  try {
    mongoClient = await MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true });
    console.log('MongoDB连接成功,准备启动定时任务');
  } catch (err) {
    console.error('MongoDB初始化失败:', err);
    process.exit(1); // 连接失败直接退出脚本
  }
}

const processAllSymbols = async () => {
  // 每次定时任务触发时,重置cnt为0,确保从第一个符号开始处理
  let cnt = 0;

  const processSingleSymbol = async () => {
    if (cnt >= symbols.length) {
      console.log('所有符号数据处理完成');
      return;
    }

    const currentSymbol = symbols[cnt];
    try {
      const res = await fetch(`https://api.binance.com/api/v3/klines?symbol=${currentSymbol}&interval=30m&limit=1`);
      const data = await res.json();

      // 先检查API返回是否为数组,避免无效数据导致崩溃
      if (!Array.isArray(data)) {
        console.error(`获取${currentSymbol}数据失败,API返回:`, data);
        cnt++;
        setTimeout(processSingleSymbol, 3000);
        return;
      }

      // 转换数据格式
      const formattedData = 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(`处理完成${currentSymbol}:`, formattedData);
      await saveToDatabase(formattedData, currentSymbol);
      cnt++;
      // 延迟3秒处理下一个符号,避免触发API限流
      setTimeout(processSingleSymbol, 3000);
    } catch (err) {
      console.error(`处理${currentSymbol}时出错:`, err);
      cnt++;
      setTimeout(processSingleSymbol, 3000);
    }
  };

  await processSingleSymbol();
};

const saveToDatabase = async (formattedData, symbolName) => {
  const today = new Date();
  // 格式化日期时间,确保格式统一(比如1月→01,9点→09)
  const date = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
  const time = `${String(today.getHours()).padStart(2, '0')}:${String(today.getMinutes()).padStart(2, '0')}:${String(today.getSeconds()).padStart(2, '0')}`;
  const dateTime = `${date} ${time}`;

  try {
    const db = mongoClient.db('CryptoCurrencies');
    const collection = db.collection(symbolName);
    const insertResult = await collection.insertOne({
      Name: symbolName,
      Array: formattedData,
      Date: dateTime
    });
    console.log(`成功插入1条数据到${symbolName}集合,ID: ${insertResult.insertedId}`);
  } catch (error) {
    console.error(`存入${symbolName}数据失败:`, error);
  }
};

// 先初始化MongoDB连接,再启动每小时的定时任务
initMongoConnection().then(() => {
  // 每小时整点执行一次(原代码的*/0 * * * *是每分钟执行,应该是0 * * * *才是每小时)
  const job = schedule.scheduleJob('0 * * * *', processAllSymbols);
  console.log(`定时任务已启动,将在每小时整点执行`);
});

关键优化点说明

  1. 重置cnt变量:把cnt放到processAllSymbols函数内部,每次定时任务触发都会重置为0,确保每次都从第一个符号开始处理。
  2. 复用MongoDB连接:原来的代码每次插入都创建新连接,这会造成不必要的性能损耗,现在改为初始化时建立一次连接,后续复用。
  3. 添加API返回校验:增加了Array.isArray(data)的判断,即使API返回错误信息,脚本也能继续处理下一个符号,不会直接崩溃。
  4. 使用async/await简化异步逻辑:相比原来的Promise链式调用,代码可读性更高,调试也更方便。
  5. 修正定时任务规则:原代码的*/0 * * * *是每分钟执行一次,你需要的是每小时执行,所以改成0 * * * *(整点触发)。
  6. 格式化日期时间:用padStart确保日期和时间的格式统一,避免出现类似"2024-2-3 8:5:3"这种不规范的格式。

内容的提问来源于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:12:41