Node.js多MySQL查询执行完毕后如何终止连接?求最优方案
解决Node.js异步API调用与MySQL查询后的连接关闭问题
兄弟,我太懂你这种异步新手的崩溃了——同步循环跑得飞快,结果异步请求和数据库查询还在后台慢悠悠处理,你提前关连接直接给你炸锅对吧?咱们先把问题拆明白,再给你靠谱的解决方案:
问题根源
你的request(API调用)和connection.query(数据库操作)都是异步函数,但外层的for循环是同步执行的。循环跑完立刻就调用connection.end(),但此时所有API请求和数据库查询都还没完成,自然会报错。
关于Waterfall模式的疑问
Waterfall(瀑布流)是串行执行任务——必须等前一个任务完全结束,才会启动下一个。它适合任务之间有依赖的场景(比如前一个API的返回结果是后一个API的参数)。但你的场景是循环调用10个无依赖的API,用waterfall会让任务一个接一个跑,效率极低,所以完全不适用。咱们应该用「并行等待」的模式,也就是Promise.all。
解决方案:用Promise统一管理异步任务
核心思路是把所有异步操作包装成Promise,然后用Promise.all等待所有任务完成后,再关闭数据库连接。这里给你两种写法,选你顺手的来:
写法1:Promise链式调用(适合刚入门Promise的同学)
const mysql = require('mysql'); const request = require('request'); // 先把request包装成Promise(把回调转成Promise风格) const requestPromise = (url) => { return new Promise((resolve, reject) => { request(url, (error, response, body) => { if (error) reject(error); else resolve(body); }); }); }; // 把connection.query也包装成Promise const queryPromise = (connection, sql, params) => { return new Promise((resolve, reject) => { connection.query(sql, params, (error, results) => { if (error) reject(error); else resolve(results); }); }); }; // 初始化数据库连接 const connection = mysql.createConnection({ host: '你的主机地址', user: '用户名', password: '密码', database: '数据库名' }); // 第一步:收集所有API请求的Promise const apiPromises = []; for (let i = 0; i < 10; i++) { // 这里一定要用let!避免闭包陷阱(用var的话所有回调里的i都是10) const url = `http://api.com?i=${i}`; apiPromises.push(requestPromise(url)); } // 第二步:等待所有API请求完成,再处理数据库查询 Promise.all(apiPromises) .then((bodies) => { // 收集所有数据库更新的Promise const queryPromises = []; bodies.forEach((body) => { const data = JSON.parse(body); for (const el in data) { queryPromises.push( queryPromise( connection, "UPDATE table SET col = ? WHERE symbol = ?", [data[el].col, el] ) ); } }); // 等待所有数据库查询完成 return Promise.all(queryPromises); }) .then(() => { console.log('所有API调用和数据库更新都完成了!'); connection.end(); // 终于可以安全关连接了 }) .catch((error) => { console.error('执行出错了:', error); // 哪怕出错也要关连接!不然会导致数据库连接泄漏 connection.end(); });
写法2:async/await(更简洁,可读性更高)
如果你已经熟悉async/await,这个写法会更直观:
const mysql = require('mysql'); const request = require('request'); // 同样先包装异步操作成Promise const requestPromise = (url) => { return new Promise((resolve, reject) => { request(url, (error, response, body) => { if (error) reject(error); else resolve(body); }); }); }; const queryPromise = (connection, sql, params) => { return new Promise((resolve, reject) => { connection.query(sql, params, (error, results) => { if (error) reject(error); else resolve(results); }); }); }; // 主逻辑用async函数包裹 const main = async () => { const connection = mysql.createConnection({ host: '你的主机地址', user: '用户名', password: '密码', database: '数据库名' }); try { // 等待所有API请求完成 const apiPromises = []; for (let i = 0; i < 10; i++) { const url = `http://api.com?i=${i}`; apiPromises.push(requestPromise(url)); } const bodies = await Promise.all(apiPromises); // 等待所有数据库更新完成 const queryPromises = []; bodies.forEach(body => { const data = JSON.parse(body); for (const el in data) { queryPromises.push( queryPromise(connection, "UPDATE table SET col = ? WHERE symbol = ?", [data[el].col, el]) ); } }); await Promise.all(queryPromises); console.log('所有任务完成,关闭连接'); connection.end(); } catch (error) { console.error('执行出错:', error); connection.end(); } }; // 启动主函数 main();
关键提醒
- 用let代替var:循环里一定要用let声明i,不然会因为闭包陷阱导致所有request回调里的i都是最后一个值(10)。
- 错误处理不能少:哪怕某个API或查询出错,也要记得关闭数据库连接,避免连接泄漏。
- 批量操作优化:如果你的数据库更新数量很大,也可以考虑把多个UPDATE合并成批量操作,减少数据库请求次数(不过这是额外优化,先解决当前问题再说)。
内容的提问来源于stack exchange,提问作者Marcin Wasilewski
相关产品推荐
相关产品推荐

