ExpressJS项目JavaScript代码未按自上而下顺序执行问题咨询
问题核心原因
- 你用到的
db.query是异步非阻塞接口:调用后不会等待数据库返回结果,会直接继续执行后续的同步代码,只有等数据库查询完成后,才会把回调函数推入事件队列执行,所以才出现了先打印latest_row_id 1,后打印result4 2的执行顺序。 - 你把依赖查询结果的变量计算、插入SQL逻辑写在了回调外部,导致拿到的是初始化的默认值
latestRowId = 1,计算出来的newRowID = 2和数据库里已有的ID冲突,触发主键重复报错。
修复方案
方案1:回调嵌套(最简单适配现有写法)
把依赖第一个查询结果的逻辑全部移到第一个db.query的回调内部即可:
//Create New Open Market under the game let latestRowId = 1; var sqlQuery2 = "SELECT ID FROM markets ORDER BY LAST_EDITED DESC LIMIT 1"; db.query(sqlQuery2, [], function(err, result4) { if (err){ console.log("Error during /openBetoBetoMarket. Proc_4" + err); return res.status(500).json({ msg: "Server Error /openBetoBetoMarket. gameID:" + gameID }); } console.log("result4", result4[0].ID) latestRowId = result4[0].ID // 后续逻辑全部移到这里 console.log("latest_row_id", latestRowId); const newRowID = latestRowId + 1 const newMarketId = result[0].MARKET_ID + 1 const newGameId = gameID const newDescription = gameTitle console.log("New row id", newRowID, "new market id", newMarketId) const sqlQuery = "INSERT INTO markets (ID, MARKET_ID, GAME_ID, DESCRIPTION, LAST_EDITED, WRITER) VALUES (?, ?, ?, ?, NOW(), CURRENT_USER);" db.query(sqlQuery, [newRowID, newMarketId, newGameId, newDescription], (err, result4) => { if (err){ console.log("Error during /openBetoBetoMarket. Proc_5 " + err); return res.status(500).json({ msg: "Server Error /openBetoBetoMarket. gameID:" + gameID }); } else if (result4.affectedRows > 0){ console.log("Created new open market for /openBetoBetoMarket. MarketID: " + newMarketId); return res.status(200).json({msg: "Created New Open Market", marketID: newMarketId, isOpen: 1}) } else { console.log("Error during /openBetoBetoMarket. Proc_5. Inserted but no affected Rows"); return res.status(500).json({ msg: "Server Error /openBetoBetoMarket. gameID:" + gameID }); } }) });
方案2:Promise + async/await 写法(更易维护,避免回调地狱)
如果你的数据库驱动支持Promise版本的调用,或者可以用util.promisify包装db.query,可以用更清晰的类同步逻辑写法:
const { promisify } = require('util'); const query = promisify(db.query).bind(db); // 把callback形式的query转为Promise形式 // 业务逻辑写在async函数内 async function createMarket() { try { const sqlQuery2 = "SELECT ID FROM markets ORDER BY LAST_EDITED DESC LIMIT 1"; const result4 = await query(sqlQuery2, []); const latestRowId = result4[0].ID; console.log("latest_row_id", latestRowId); const newRowID = latestRowId + 1 const newMarketId = result[0].MARKET_ID + 1 const newGameId = gameID const newDescription = gameTitle console.log("New row id", newRowID, "new market id", newMarketId) const sqlQuery = "INSERT INTO markets (ID, MARKET_ID, GAME_ID, DESCRIPTION, LAST_EDITED, WRITER) VALUES (?, ?, ?, ?, NOW(), CURRENT_USER);" const insertRes = await query(sqlQuery, [newRowID, newMarketId, newGameId, newDescription]) if (insertRes.affectedRows > 0) { console.log("Created new open market for /openBetoBetoMarket. MarketID: " + newMarketId); return res.status(200).json({msg: "Created New Open Market", marketID: newMarketId, isOpen: 1}) } throw new Error("Inserted but no affected Rows") } catch (err) { console.log("Error during /openBetoBetoMarket: ", err) return res.status(500).json({ msg: "Server Error /openBetoBetoMarket. gameID:" + gameID }); } } createMarket()
额外优化建议
- 不建议自行计算主键ID,建议将数据库表的ID字段设置为自增主键,插入时不需要传ID字段,数据库会自动生成不重复的ID,从根本上避免主键冲突问题。
- 并发场景下即使按上面的异步逻辑写,也可能出现两个请求同时查到相同的
latestRowId,计算出相同的newRowID导致冲突,用自增主键可以完全规避这个问题。
内容的提问来源于stack exchange,提问作者Joebert Rosales
相关产品推荐
相关产品推荐

