Node.js MySQL参数化查询传null时错误无法捕获致应用崩溃
问题解答
为什么错误无法被外层catch块捕获
核心原因是错误抛出的位置脱离了Promise的错误捕获链路:
new Promise只能自动捕获初始化阶段同步执行的代码抛出的错误,你在executor函数里调用pool.getConnection是异步操作,传入的连接回调会等数据库连接建立后才会被推入事件队列执行,这时候Promise初始化流程早就结束了,回调里抛出的错误不会被Promise自动处理。- 当传入的
art为null时,代码在构造params数组读取art.title的瞬间就会抛出TypeError,这个错误发生在异步回调内部,既没有被手动try/catch包裹,也没有通过reject方法向外传递,直接变成了进程级的未捕获异常。 - 外层的
try...catch只能捕获await对应的Promise被reject的错误,根本接不到这个直接抛在事件循环里的异常,最终就会导致应用崩溃。 - 额外隐患:当前的Promise封装所有错误分支都用了
resolve(null),就算是Promise可捕获范围内的错误,也不会触发外层catch,只会拿到null返回值;同时如果SQL执行后affectedRows不大于0,代码既不resolve也不reject,会导致await永久挂起。
修复方法
核心思路是把所有可能抛错的路径都纳入Promise的reject链路,保证异常能被await感知到,最小改动的修复代码如下:
/* insert db */ function addGrabsArticle(art) { return new Promise((resolve, reject) => { // 提前做入参校验,非法参数直接返回错误 if (!art || typeof art !== 'object') { return reject(new Error('插入失败:文章参数为无效对象')); } pool.getConnection((err, conn) => { // 给整个连接回调包一层try catch,捕获所有同步错误 try { if (err) { return reject(err); } const sql = "insert into `archives`(`title`,`pubdate`,`type`,`cat`,`seokey`,`seodesc`,`content`,`images`,`isdel`,`ispublish`) values(?,?,?,?,?,?,?,?,?,?)"; const params = [ art.title, `${Date.now()}`, 1, // article 2, // coding art.seokey, art.seodesc, art.content, JSON.stringify(art.images), 0, 0 ]; conn.query(sql, params, (queryErr, result) => { try { pool.releaseConnection(conn); if (queryErr) { return reject(queryErr); } if (result.affectedRows > 0) { return resolve(result.insertId); } reject(new Error('插入失败:无受影响的数据行')); } catch (innerErr) { reject(innerErr); } }); } catch (callbackErr) { // 捕获到错误时如果已经拿到连接,先释放连接避免泄漏 if (conn) pool.releaseConnection(conn); reject(callbackErr); } }); }); }
优化建议
- 长期来看建议替换为自带Promise封装的数据库驱动(比如
mysql2/promise),直接用async/await写数据库逻辑,不需要手动封装回调嵌套的Promise,能从根源上避免这类异步上下文脱离错误捕获的问题,代码可读性也更高。 - 修改后不管是参数非法、数据库连接错误、SQL执行错误还是数据处理阶段的同步报错,都会被Promise reject,外层的try/catch可以正常捕获,不会再出现应用崩溃的问题。
内容的提问来源于stack exchange,提问作者wudics
相关产品推荐
相关产品推荐

