Node+Express从数据库获取记录集失败问题及解决
你遇到的核心问题其实是异步操作的返回值处理,刚接触Node和Express的时候很容易踩这个坑,咱们逐个拆解你的代码问题,再讲正确的解法:
1. 为什么getAllUser函数返回空白?
connectionPool.query是异步操作,它的回调函数要等数据库查询完成后才会执行。但你的getAllUser函数会在query发起请求后立刻返回,根本等不到回调里的result.recordset,所以函数实际返回的是undefined,路由里res.send(undefined)自然就显示空白了。
2. 为什么test5会报循环结构转JSON错误?
你在test5里返回的是connectionPool.query()的调用结果,这个结果其实是mssql的Query对象(或Promise实例),而不是你想要的result.recordset。这个对象内部包含循环引用的结构(比如连接池的自身引用),当Express的res.send尝试把它序列化成JSON时,就会抛出TypeError: Converting circular structure to JSON的错误。虽然控制台能输出回调里的result,但那是回调函数内部的操作,和函数的返回值没关系。
3. 正确的解法:用Async/Await处理异步
mssql的ConnectionPool.query方法本身支持Promise,所以我们可以用async/await来优雅地处理异步操作,既避免回调地狱,又能正确拿到返回值:
步骤1:编写返回Promise的查询函数
// 封装查询函数,返回Promise function getAllUser() { try { // query方法返回Promise,直接返回它即可 return connectionPool.query("SELECT * FROM User"); } catch (err) { // 捕获同步错误,返回错误信息的Promise return Promise.reject(err.message); } }
步骤2:在路由中使用Async/Await
Express支持异步路由处理函数,只要给路由回调加上async,然后用await等待查询结果:
app.get("/users", async (req, res) => { try { const result = await getAllUser(); // 只发送我们需要的recordset数据 res.send(result.recordset); } catch (err) { // 捕获异步错误,返回500状态码和错误信息 res.status(500).send(err); } });
补充:为什么getAllUser2能正常工作?
因为getAllUser2用了回调函数,把数据传递的逻辑放在了异步操作完成后的回调里,路由里也是等回调触发后才调用res.send,相当于手动等待了异步操作完成,所以能正确拿到数据。不过回调写法在复杂场景下容易陷入回调地狱,用async/await会更清晰易读。
内容的提问来源于stack exchange,提问作者naitunix

