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

JavaScript异步执行问题:数据库插入函数外键获取失败

问题排查与解决方案

问题根源

你的代码里connection.query是异步非阻塞操作,updateScratcherGameData中遍历uniqueGameData发起的插入请求,并不会等所有插入完成后再执行updateScratcherRowData。也就是说,当updateScratcherRowData开始查询game_id时,部分游戏数据可能还没写入game表,导致rows[0]为undefined,进而触发Cannot read properties of undefined (reading 'game_id')错误。

修复方案

1. 用Promise封装异步操作,确保所有插入完成后再执行后续逻辑

把每个数据库操作封装成Promise,然后用Promise.all等待所有游戏数据插入完成,再调用updateScratcherRowData:

// 封装查询为Promise,统一处理异步逻辑
function query(sql, params) {
  return new Promise((resolve, reject) => {
    connection.query(sql, params, (err, rows) => {
      if (err) reject(err);
      else resolve(rows);
    });
  });
}

async function updateScratcherGameData(cleanData) {
  // 遍历所有唯一游戏数据,生成插入任务
  const insertTasks = uniqueGameData.map(async (e) => {
    // 检查游戏是否已存在
    const checkRows = await query(`SELECT name FROM game WHERE name=?;`, [e["name"]]);
    
    if (checkRows.length === 0) {
      // 插入新游戏
      await query(
        `INSERT INTO game (name,scratcher_lotto_id,gametype_id,active) VALUES (?,?,?,?);`,
        [e["name"], e["scratcher_id"], 1, 1]
      );
      console.log(`已插入游戏: ${e["name"]}`);
    }
  });

  // 等待所有插入操作完成
  await Promise.all(insertTasks);
  
  // 所有游戏数据都写入后,再执行数据插入
  await updateScratcherRowData(cleanData);
}

// 同步改造第二个函数,避免内部异步问题
async function updateScratcherRowData(cleanData) {
  const insertTasks = cleanData.map(async (e) => {
    // 查询对应的game_id
    const gameRows = await query(
      `SELECT game_id FROM game WHERE name=? AND scratcher_lotto_id=?;`,
      [e["name"], e["scratcher_id"]]
    );
    
    if (gameRows.length === 0) {
      throw new Error(`未找到游戏 ${e["name"]} 的game_id,请检查数据是否正确`);
    }

    // 插入scratcher数据
    await query(
      `INSERT INTO scratcher_data (scrape_date,game_id,prize,odds,remaining_prize,total_prize) VALUES (TIMESTAMP(NOW()),?,?,?,?,?);`,
      [gameRows[0]["game_id"], e["prize"], e["odds"], e["tickets_left"], e["tickets_remaining"]]
    );
  });

  await Promise.all(insertTasks);
}

2. 必须修复的SQL注入风险

你原来的代码直接拼接SQL字符串,存在严重的SQL注入漏洞,上面的示例已经改成了参数化查询,这是生产环境必须遵循的规范,能避免恶意数据破坏数据库。

额外说明

单独运行时没问题,是因为你手动等待了足够时间,让所有异步插入操作都完成了;而连续调用时,异步操作的顺序问题就立刻暴露出来。用async/await和Promise能精准控制异步流程的执行顺序。

内容的提问来源于stack exchange,提问作者doe doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:17:16