Node.js中如何同步执行MySQL查询以得到预期输出结果?
解决Node.js MySQL异步查询导致输出顺序不符的问题
问题描述
我有如下代码:
app.get('/showtable1', (req, res) => { console.log("yo ho"); mysqlconnection.query('SELECT d_name FROM datacenters', (err, rows, fields) => { if (!err) { var array = []; var tier = ['Class 0','Class 1','Class 2','Class 3']; var str = ''; Object.keys(rows).forEach(function(key) { var row = rows[key]; array.push(row.d_name); }); for( var i = 0 ; i<array.length ; i++) { str += '<tr><td>'+array[i]+'</td>'; console.log('sfsf'); for (var j=0 ; j< tier.length ; j++) { let query1 = "SELECT * FROM `datacenters` WHERE d_name = '"+array[i]+"' AND d_tiers LIKE '%"+tier[j]+"%'" mysqlconnection.query(query1,(err,rows) => { if(!err) { if(rows.length !=0) { console.log(1); } else { console.log(0); } } }); } } res.send(rows); } else console.log(err); }) });
当前代码输出为:
yo ho sfsf sfsf sfsf 1 1 0 0 1 1 1 0 1 1 1 0
但我需要的输出顺序是:
yo ho sfsf 1 1 0 0 sfsf 1 1 1 0 sfsf 1 1 1 0
当前输出顺序不符是因为代码异步执行,我需要将其改为同步执行,请问该如何实现?
解决方案
没错,问题根源就是Node.js中MySQL的查询是异步回调模式,内层的tier查询会在外层循环的sfsf全部打印完之后才陆续返回结果。要实现你要的顺序,最优雅的方式是用Promise + async/await把异步操作转换成同步风格的代码,这也是现在Node.js处理异步逻辑的标准做法。
1. 先把MySQL查询包装成Promise
原生的mysqlconnection.query是回调风格,我们先把它封装成返回Promise的函数,这样才能用await等待执行完成:
// 封装Promise版的查询函数 function queryPromise(sql, params = []) { return new Promise((resolve, reject) => { mysqlconnection.query(sql, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); }
2. 用async/await重构路由逻辑
把路由的处理函数改成async函数,然后在需要等待查询结果的地方加上await,这样代码就会按顺序执行:
app.get('/showtable1', async (req, res) => { try { console.log("yo ho"); // 等待第一个查询完成,拿到所有数据中心名称 const rows = await queryPromise('SELECT d_name FROM datacenters'); // 用map简化数组生成,比forEach更简洁 const array = rows.map(row => row.d_name); const tier = ['Class 0','Class 1','Class 2','Class 3']; let str = ''; for( let i = 0 ; i<array.length ; i++) { str += '<tr><td>'+array[i]+'</td>'; console.log('sfsf'); // 逐个等待每个tier的查询完成,保证顺序 for (let j=0 ; j< tier.length ; j++) { // 这里改成参数化查询,避免SQL注入风险 const resultRows = await queryPromise( 'SELECT * FROM `datacenters` WHERE d_name = ? AND d_tiers LIKE ?', [array[i], `%${tier[j]}%`] ); console.log(resultRows.length !== 0 ? 1 : 0); } // 可选:每个数据中心的结果打印完后换行,让输出更清晰 console.log(''); } res.send(rows); } catch (err) { console.error(err); // 错误时给客户端返回500状态码和提示 res.status(500).send('服务器内部错误'); } });
重要提醒:避免SQL注入
你原来的代码直接把变量拼接到SQL语句里,这存在严重的SQL注入风险。上面的代码已经改成了参数化查询,MySQL会自动处理参数的转义,彻底避免这个问题,这是生产环境必须注意的点。
修改之后,代码就会严格按照你期望的顺序执行:打印sfsf之后,等待该数据中心的4个tier查询依次完成并输出1/0,再进入下一个循环打印sfsf,以此类推。
内容的提问来源于stack exchange,提问作者amar
相关产品推荐
相关产品推荐

