如何从Promise取值并存入Express的res.locals?动态导航栏实现疑问
解决Express中异步获取菜单并存入res.locals的问题
你遇到的核心问题是异步操作的执行顺序没处理对——你调用menus.getMenu后立刻执行了next(),但数据库查询是异步的,这时候query.rows还没返回,自然没法把值存入res.locals。好在Bookshelf/Knex本身就支持Promise,完全不需要重写它的方法,只需要调整异步逻辑的控制方式就行。
方案一:在getMenu内部处理异步并触发后续流程
这种方式把异步结果的处理和next()的调用都放在getMenu函数里,逻辑更内聚:
修改menu.js
var bookshelf = require('../config/bookshelf'); exports.getMenu = function(req, res, next) { var sql = "select * from menus"; // 利用Knex返回的Promise处理查询结果 bookshelf.knex.raw(sql) .then(function(query) { // 把菜单数据直接存入res.locals,供后续模板/中间件使用 res.locals.menus = query.rows; // 异步操作完成后,再调用next()推进请求流程 next(); }) .catch(function(err) { // 必须处理数据库查询错误,避免请求挂起 next(err); }); };
修改server.js的中间件
因为getMenu内部会在异步完成后调用next(),所以外部不要再提前调用:
var express = require('express'); var menus = require('./config/menu'); require('./config/passport'); var app = express(); app.use(function(req, res, next) { res.locals.user = req.user ? req.user.toJSON() : null; // 用可选链简化用户登录状态判断 if (res.locals.user?.id) { // 调用getMenu,内部会在异步完成后执行next menus.getMenu(req, res, next); } else { // 用户未登录时直接推进流程 next(); } });
方案二:让getMenu返回Promise,在中间件中等待结果
如果你希望把异步控制逻辑留在server.js里,可以让getMenu返回查询的Promise,然后用async/await等待结果:
修改menu.js
var bookshelf = require('../config/bookshelf'); exports.getMenu = function() { var sql = "select * from menus"; // 返回查询Promise,resolve结果为菜单行数据 return bookshelf.knex.raw(sql).then(function(query) { return query.rows; }); };
修改server.js的中间件
把中间件改成异步函数,用await等待查询完成:
var express = require('express'); var menus = require('./config/menu'); require('./config/passport'); var app = express(); // 标记为异步中间件 app.use(async function(req, res, next) { res.locals.user = req.user ? req.user.toJSON() : null; if (res.locals.user?.id) { try { // 等待菜单查询完成,把结果存入res.locals res.locals.menus = await menus.getMenu(); } catch (err) { // 捕获查询错误并传递给错误处理中间件 return next(err); } } // 无论是否登录,最后都推进请求流程 next(); });
为什么你的原代码不生效?
你原代码里的getMenu只是把menus变量赋值为一个Promise,但既没有把Promise的结果关联到res.locals,也没有等待异步操作完成就执行了外部的next()——这导致后续中间件或模板渲染时,菜单数据还没被存入res.locals。
你看到的Promise示例思路是对的,只是不需要重写Bookshelf的方法,因为它本身就返回Promise,你只需要像上面这样利用这个Promise来控制异步流程即可。
内容的提问来源于stack exchange,提问作者Ulisses
相关产品推荐
相关产品推荐

