【已解决】Nodejs router中next的异步调用问题

【已解决】Nodejs router中next的异步调用问题

LZ是初学者,最近在router的代码中遇到如下的异步加载问题,希望高手来指导一下: 我的代码如下:

// 加载侧边栏
router.get(/^\/blog[\/]?/, blog.aside);
// 加载导航栏(mongoose异步查询获取)
router.get(/^\/blog[\/]?/, blog.header);
// 加载文章列表,加载完成后,执行`res.render`渲染
router.get('/blog/bloglist/:blogCategory?', blog.list);
router.get('/blog/detail/:blogCategory?/:_id', blog.detail);

由于导航栏是异步加载的,在代码执行中,可能会先执行router.get(/^\/blog[\/]?/, blog.header);的next,出现在blog.list执行完成并已经开始jade渲染的时候,导航栏的callback还没有返回,最终导致页面不显示导航栏内容。


###这里是解决方案### 很简单,对next()的理解不透彻。以为只能return next()。只需要将 blog.header中的return next()删除,在callback执行完成的地方,去调用next()就可以了

next执行的时候,可以根据callback的情况进行控制吗? 如果是利用step或者Async来控制,感觉就应该放到一个路由中,而不应该通过next来区分了


2 回复

【已解决】Nodejs Router 中 next 的异步调用问题

背景

我是一名Node.js初学者,最近在处理路由器(router)时遇到了一些异步加载的问题。具体来说,我希望在加载完所有数据后再进行页面渲染,但因为某些异步操作没有正确处理,导致页面渲染出现问题。

问题描述

我的代码如下:

// 加载侧边栏
router.get(/^\/blog[\/]?/, blog.aside);

// 加载导航栏 (使用mongoose异步查询获取)
router.get(/^\/blog[\/]?/, blog.header);

// 加载文章列表,并在加载完成后渲染页面
router.get('/blog/bloglist/:blogCategory?', blog.list);
router.get('/blog/detail/:blogCategory?/:_id', blog.detail);

由于导航栏是异步加载的,当代码执行到 blog.header 时,可能会在 blog.list 已经开始渲染的情况下,导航栏的回调函数还没有返回。这导致页面没有显示导航栏的内容。

解决方案

经过研究,我发现 next() 的使用存在一些误解。我最初认为 next() 只能在 return next() 之后调用。实际上,可以在回调函数执行完毕后调用 next()

下面是修正后的代码示例:

// 加载侧边栏
router.get(/^\/blog[\/]?/, (req, res, next) => {
    blog.aside(req, res, next);
});

// 加载导航栏 (使用mongoose异步查询获取)
router.get(/^\/blog[\/]?/, (req, res, next) => {
    blog.header(req, res, () => {
        // 在异步操作完成后再调用 next()
        next();
    });
});

// 加载文章列表,并在加载完成后渲染页面
router.get('/blog/bloglist/:blogCategory?', (req, res, next) => {
    blog.list(req, res, next);
});

router.get('/blog/detail/:blogCategory?/:_id', (req, res, next) => {
    blog.detail(req, res, next);
});

关键点解释

  1. 异步操作与 next(): 在 blog.header 中,我们添加了一个回调函数,确保在异步操作完成后才调用 next()
  2. 避免多个路由重复调用 next(): 我们将 next() 放在一个地方调用,而不是在多个路由中分别调用,这样可以确保所有异步操作完成后才继续执行下一个中间件或路由。

使用 asyncawait

如果使用 asyncawait,可以更简洁地处理异步操作:

// 加载侧边栏
router.get(/^\/blog[\/]?/, async (req, res, next) => {
    try {
        await blog.aside(req, res);
        next();
    } catch (err) {
        next(err);
    }
});

// 加载导航栏 (使用mongoose异步查询获取)
router.get(/^\/blog[\/]?/, async (req, res, next) => {
    try {
        await blog.header(req, res);
        next();
    } catch (err) {
        next(err);
    }
});

// 加载文章列表,并在加载完成后渲染页面
router.get('/blog/bloglist/:blogCategory?', async (req, res, next) => {
    try {
        await blog.list(req, res);
        next();
    } catch (err) {
        next(err);
    }
});

router.get('/blog/detail/:blogCategory?/:_id', async (req, res, next) => {
    try {
        await blog.detail(req, res);
        next();
    } catch (err) {
        next(err);
    }
});

这样可以更清晰地管理异步操作,并确保所有操作都完成后再继续执行。


在Node.js中使用Express框架时,next()函数用于将控制权传递给下一个中间件或路由处理程序。如果在异步操作(如数据库查询)完成后没有正确调用next(),会导致后续的中间件或路由处理程序不会被执行。

对于你的问题,关键在于确保在所有异步操作完成后正确地调用next()。你可以使用回调、Promises或者async/await来管理异步操作,并在操作完成后调用next()

下面是示例代码:

const express = require('express');
const router = express.Router();
const blog = require('./blog'); // 假设这是你的处理模块

// 加载侧边栏
router.get(/^\/blog[\/]?/, (req, res, next) => {
    blog.aside(req, res, next);
});

// 加载导航栏 (使用async/await)
router.get(/^\/blog[\/]?/, async (req, res, next) => {
    try {
        await blog.header(req);
        next(); // 导航栏加载完成后调用next()
    } catch (error) {
        return next(error); // 如果发生错误,则传递给错误处理中间件
    }
});

// 加载文章列表,加载完成后,执行`res.render`渲染
router.get('/blog/bloglist/:blogCategory?', async (req, res, next) => {
    try {
        await blog.list(req);
        res.render('bloglist'); // 渲染视图
    } catch (error) {
        next(error); // 传递错误给错误处理中间件
    }
});

router.get('/blog/detail/:blogCategory?/:_id', blog.detail);

module.exports = router;

在这个示例中,blog.headerblog.list 都被设计为异步函数,它们接受请求和响应对象以及next函数作为参数。当这些异步操作完成后,它们会调用next()来继续执行下一个处理程序。如果发生错误,则通过next(error)将错误传递给错误处理中间件。这样可以确保所有的异步操作都已完成,并且导航栏内容也会正常显示。

回到顶部