You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:11:04