本文详解 pg-promise 批量数据库操作中因 promise 传递不当导致的未捕获异常问题,指出 `t.batch()` 已废弃,并提供基于显式 `await` 和统一错误处理的现代事务写法。
在使用 pg-promise 构建事务性数据库操作时,一个常见陷阱是:将已执行(即已返回 Promise 实例)的查询函数直接传入 t.batch(),而非在事务上下文内按需构造 Promise。这会导致错误无法被事务层捕获,进而引发 Uncaught Exception——尤其在数据库连接失败、SQL 语法错误或约束冲突等场景下,Node.js 进程可能意外崩溃。
根本原因在于:
✅ 正确做法:所有数据库操作必须在事务回调函数内、通过事务对象 t 执行,并避免过早求值。推荐使用 async/await 显式控制流程,而非依赖已废弃的 t.batch():
// ✅ 推荐:参数化 + 可选事务上下文
const addToColumn = (tableName, columnName, entryId, amountToAdd, t = db) => {
return t.one(
'UPDATE ${table:name} SET ${column:name} = ${column:name} + ${amount:csv} WHERE id = ${id:csv} RETURNING *',
{
table: tableName,
column: columnName,
amount: amountToAdd,
id: entryId,
}
);
};
// ✅ 推荐:事务内显式 await,自动回滚 + 统一错误传播
const transfe
rEnvelopeBudgetByIds = async (req, res, next) => {
try {
const result = await db.tx(async t => {
const from = await addToColumn(
'envelopes',
'budget',
req.envelopeFromId,
-req.transferBudget,
t
);
const to = await addToColumn(
'envelopes',
'budget',
req.envelopeToId,
req.transferBudget,
t
);
return { from, to }; // 可选:返回结构化结果
});
req.updatedEnvelopes = result;
next();
} catch (err) {
// 所有错误(连接失败、SQL 错误、约束冲突)均由此处统一捕获
// pg-promise 自动回滚事务,无需手动处理
next(err);
}
};⚠️ 注意事项:
总结:pg-promise 的事务可靠性取决于 Promise 的构造时机与执行上下文。坚持“事务内构造、事务内执行、顶层捕获”的三原则,即可彻底规避未捕获异常,并获得强一致的 ACID 保障。