Node.js中for循环执行函数仅使用最后一个key的问题求助
解决循环中异步回调共享变量的问题
这是典型的闭包异步陷阱,我来帮你理清问题根源并给出修复方案:
问题原因
你用var声明的PItem和key是函数级作用域变量,而con.query是异步操作——当回调函数真正执行的时候,外层的for循环已经全部跑完了,此时PItem已经被更新为循环的最后一个key值,所以所有回调都会用这个最后值来执行SQL。
修复方案
方案1:用let替换var(最简洁)
let是块级作用域,每次循环会创建一个新的变量绑定,确保每个回调拿到的都是当前循环的key对应的PItem:
/*Global variable*/ let PItem = "Item"; for(let key in Prices){ // 这里改用let声明key PItem = key; let sql = `SELECT EXISTS(SELECT * FROM \`items\` WHERE \`name\` = "${PItem}")`; con.query(sql , function(err, result){ if(err) return console.log(err); let result2 = 0; // 建议用let声明,避免全局污染 for (var key6 in result){ for(var key7 in result[key6]) result2 = result[key6][key7]; } if(result2 == 0){ let sql = `INSERT INTO \`items\` (\`name\`, \`buy\`, \`sell\`, \`stock\`, \`maxStock\`) VALUES ("${PItem}", ${Prices[PItem].buy}, ${Prices[PItem].sell}, ${Prices[PItem].stock}, ${Prices[PItem].maxstock})`; con.query(sql, function(err, result){ if(err) return console.log(err); }); } }); }
方案2:用立即执行函数(IIFE)捕获当前变量
通过IIFE创建独立作用域,把当前循环的PItem传递进去,确保回调使用的是当前迭代的变量:
/*Global variable*/ var PItem = "Item"; for(var key in Prices){ PItem = key; // 用IIFE捕获当前的PItem (function(currentItem) { var sql = 'SELECT EXISTS(SELECT * FROM `items` WHERE `name` = "'+currentItem+'")'; con.query(sql , function(err, result){ if(err) return console.log(err); let result2 = 0; for (var key6 in result){ for(var key7 in result[key6]) result2 = result[key6][key7]; } if(result2 == 0){ var sql = 'INSERT INTO `items` (`name`, `buy`, `sell`, `stock`, `maxStock`) VALUES ("'+currentItem+'", '+Prices[currentItem].buy+', '+Prices[currentItem].sell+', '+Prices[currentItem].stock+', '+Prices[currentItem].maxstock+')'; con.query(sql, function(err, result){ if(err) return console.log(err); }); } }); })(PItem); }
方案3:改用forEach循环
forEach的每次迭代都会生成独立作用域,天然避免闭包共享变量的问题:
/*Global variable*/ let PItem = "Item"; // 遍历Prices的键数组 Object.keys(Prices).forEach(key => { PItem = key; let sql = `SELECT EXISTS(SELECT * FROM \`items\` WHERE \`name\` = "${PItem}")`; con.query(sql , function(err, result){ // 后续逻辑和之前一致 if(err) return console.log(err); let result2 = 0; for (var key6 in result){ for(var key7 in result[key6]) result2 = result[key6][key7]; } if(result2 == 0){ let sql = `INSERT INTO \`items\` (\`name\`, \`buy\`, \`sell\`, \`stock\`, \`maxStock\`) VALUES ("${PItem}", ${Prices[PItem].buy}, ${Prices[PItem].sell}, ${Prices[PItem].stock}, ${Prices[PItem].maxstock})`; con.query(sql, function(err, result){ if(err) return console.log(err); }); } }); });
重要提醒:避免SQL注入!
你现在直接把变量拼接到SQL字符串里,存在严重的SQL注入风险!一定要用参数化查询,示例如下:
// 查询的参数化写法 let sql = 'SELECT EXISTS(SELECT * FROM `items` WHERE `name` = ?)'; con.query(sql, [PItem], function(err, result){ // 处理结果 }); // 插入的参数化写法 let insertSql = 'INSERT INTO `items` (`name`, `buy`, `sell`, `stock`, `maxStock`) VALUES (?, ?, ?, ?, ?)'; con.query(insertSql, [PItem, Prices[PItem].buy, Prices[PItem].sell, Prices[PItem].stock, Prices[PItem].maxstock], function(err, result){ if(err) return console.log(err); });
内容的提问来源于stack exchange,提问作者Nikita Ivanov
相关产品推荐
相关产品推荐

