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

Express新手求助:如何用Promise实现MongoDB Find查询顺序执行?

解决MongoDB异步查询的顺序执行问题

嘿,刚接触Express和MongoDB的话,异步操作的顺序确实容易让人头疼!你现在的问题是两个find查询并行执行,导致第二个查询里的length变量还没被第一个查询赋值,分页计算自然就出错了。其实MongoDB的驱动已经支持Promise了,我们可以用Promise链式调用或者更直观的async/await来解决这个问题,后者对新手来说更容易理解。

为什么你的代码会出问题?

你现在用的是回调函数写法,两个exec()会同时发起数据库请求,Node.js不会等第一个查询完成再执行第二个。所以当第二个查询里用到length的时候,第一个查询的回调还没执行,length还是初始的undefined,这就导致后续的numOfProducts也是undefined,分页计算肯定不对。

解决方案:用async/await简化异步流程

async/await是ES2017的语法,能让异步代码看起来像同步代码,非常适合处理这种需要顺序执行的异步操作。我们只需要把路由处理函数改成async函数,然后用await等待每个查询完成即可。

修改后的代码示例:

app.get("/cryptonews/:page", async function (req, res) { 
  try {
    let page = req.params.page; 
    const perpage = 10; 
    var page_name; 

    // 第一个查询:获取总数据量,等待查询完成后再往下执行
    const allNews = await news.find().sort({newsDate: -1}).exec();
    console.log("1st"); 
    const length = allNews.length; 
    console.log("length2", length); 

    // 第二个查询:获取分页数据(此时第一个查询已经完成,length有正确值)
    const paginatedNews = await news.find()
      .sort({newsDate: -1})
      .skip((perpage * page) - perpage) 
      .limit(perpage)
      .exec();
    console.log("2nd"); 

    const numOfProducts = length; 
    console.log("length", numOfProducts); 

    // 后续的渲染逻辑保持不变,只是把news变量换成paginatedNews
    if(req.session.user != null){ 
      if(req.session.user.address){ 
        const smartContrat = this.web3.eth.contract(this.abi).at(contractAddress); 
        const balance = smartContrat.balanceOf(req.session.user.address) / 1e18; 
        res.render('cryptonews', { 
          sess: req.session, 
          news: paginatedNews, 
          imageurl, 
          balance: balance, 
          currentPage: page, 
          pages: Math.ceil(numOfProducts / perpage), 
          numOfResults: numOfProducts, 
          page_name:'news' 
        }); 
      } else{ 
        res.render('cryptonews', { 
          sess: req.session, 
          news: paginatedNews, 
          imageurl, 
          currentPage: page, 
          pages: Math.ceil(numOfProducts / perpage), 
          numOfResults: numOfProducts, 
          page_name:'news' 
        }); 
      } 
    } else{ 
      res.render('cryptonews', { 
        sess: req.session, 
        news: paginatedNews, 
        imageurl, 
        currentPage: page, 
        pages: Math.ceil(numOfProducts / perpage), 
        numOfResults: numOfProducts, 
        page_name:'news' 
      }); 
    }
  } catch (err) {
    // 统一捕获所有异步操作的错误,避免服务器崩溃
    console.error(err);
    res.status(500).send('服务器出错了');
  }
});

关键说明:

  1. async函数:把路由处理函数声明为async function,这样里面就可以使用await关键字。
  2. await关键字:每个await都会等待后面的Promise完成,所以第一个查询完成后才会执行第二个查询,length就能正确获取到总数据量。
  3. 错误处理:用try/catch包裹所有异步操作,这样任何查询出错都会被捕获,我们可以统一返回错误响应。

如果你想理解Promise的基础用法,也可以用链式调用的写法:

app.get("/cryptonews/:page", function (req, res) { 
  let page = req.params.page; 
  const perpage = 10; 
  var page_name; 

  // 第一个查询返回Promise,用then处理结果
  news.find().sort({newsDate: -1}).exec()
    .then(allNews => {
      console.log("1st"); 
      const length = allNews.length; 
      console.log("length2", length); 
      // 第一个查询完成后,返回第二个查询的Promise
      return news.find()
        .sort({newsDate: -1})
        .skip((perpage * page) - perpage) 
        .limit(perpage)
        .exec()
        .then(paginatedNews => ({ paginatedNews, length }));
    })
    .then(({ paginatedNews, length }) => {
      console.log("2nd"); 
      const numOfProducts = length; 
      console.log("length", numOfProducts); 
      // 这里写渲染逻辑,和之前一致
    })
    .catch(err => {
      console.error(err);
      res.status(500).send('服务器出错了');
    });
});

不过对于新手来说,async/await的写法可读性更高,推荐优先使用。

内容的提问来源于stack exchange,提问作者Dv Thiyanesh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:50:41