【问题标题】:Node.js - wait for multiple async callsNode.js - 等待多个异步调用
【发布时间】:2013-08-03 05:27:31
【问题描述】:

我试图在渲染 Jade 模板之前进行多个 MongoDB 查询,但我无法完全弄清楚如何等到所有 Mongo 查询完成后再渲染模板。

exports.init = function(req, res){


    var NYLakes = {};
    var NJLakes = {};
    var filterNY = {"State" : "NY"};
    db.collection('lakes').find(filterNY).toArray(function(err, result) {
        if (err) throw err;
        NYLakes = result;
    });

    var filterNJ = {"State" : "NJ"};
    db.collection('lakes').find(filterNJ).toArray(function(err, result) {
        if (err) throw err;
        NJLakes = result;
    });

    res.render('explore/index', {
            NYlakes: NYLakes,
            NJlakes: NJLakes
    });

};

【问题讨论】:

    标签: javascript node.js mongodb


    【解决方案1】:

    我是下划线/lodash的忠实粉丝,所以我通常使用_.after,它创建了一个只有在被调用一定次数后才会执行的函数。

    var finished = _.after(2, doRender);
    
    asyncMethod1(data, function(err){
      //...
      finished();
    });
    
    asyncMethod2(data, function(err){
      //...
      finished();
    })
    
    function doRender(){
      res.render(); // etc
    } 
    

    由于 javascript 提升了使用 function funcName() 语法定义的函数的定义,因此您的代码自然会读取:从上到下。

    【讨论】:

      【解决方案2】:

      假设您希望并行运行这两个操作,而不是等待一个操作完成后再开始下一个操作,您需要跟踪每个回调中完成了多少操作。

      在原始 node.js javascript 中,一种方法是:

      exports.init = function(req, res){
          var NYLakes = null;
          var NJLakes = null;
          var filterNY = {"State" : "NY"};
      
          db.collection('lakes').find(filterNY).toArray(function(err, result) {
              if (err) throw err;
              NYLakes = result;
              complete();
          });
      
          var filterNJ = {"State" : "NJ"};
          db.collection('lakes').find(filterNJ).toArray(function(err, result) {
              if (err) throw err;
              NJLakes = result;
              complete();
          });
      
          function complete() {
              if (NYLakes !== null && NJLakes !== null) {
                  res.render('explore/index', {
                      NYlakes: NYLakes,
                      NJlakes: NJLakes
                  });
              }
          }
      
      };
      

      基本上这里发生的情况是,您在每个操作结束时检查它们是否都已完成,然后您完成操作。

      如果您正在做很多此类事情,请查看 async 库作为一个工具示例,以便更轻松地管理此类事情。

      【讨论】:

      • 这会有竞争条件。如果两个异步操作都在调用 complete() 之前执行,那么它们都可以使用必要的条件调用它,以在 complete() 中执行 if 语句的主体。不太可能,但可能。
      • @ghert85 在多线程环境中你是对的,但 node.js 是单线程的。所以代码在返回事件循环之前不能被中断,并且这种竞争条件不会发生。
      【解决方案3】:

      你可以使用async模块:

      var states = [{"State" : "NY"},{"State" : "NJ"}];
      
      var findLakes = function(state,callback){
        db.collection('lakes').find(state).toArray(callback);
      }
      
      async.map(states, findLakes , function(err, results){
          // do something with array of results
      });
      

      【讨论】:

        【解决方案4】:

        等待https://github.com/luciotato/waitfor

        使用 Wait.for:

        exports.init = function(req, res){
        
            var NYLakes = {};
            var NJLakes = {};
        
            var coll = db.collection('lakes');
        
            var filterNY = {"State" : "NY"};
            var a = wait.forMethod(coll,'find',filterNY);
            NYLakes = wait.forMethod(a,'toArray');
        
            var filterNJ = {"State" : "NJ"};
            var b = wait.forMethod(coll,'find',filterNJ);
            NJLakes = wait.forMethod(b,'toArray');
        
            res.render('explore/index',
                {
                    NYlakes: NYLakes,
                    NJlakes: NJLakes
                }
            );
        
        };
        

        使用 wait.for 并行映射并行请求:

        exports.init = function(req, res){
        
            var coll = db.collection('lakes');
        
            //execute in parallel, wait for results
            var result = wait.parallel.map(
                            [{coll:coll,filter:{"State" : "NY"}}
                            , {coll:coll,filter:{"State" : "NJ"}}]
                            , getData);
        
            res.render('explore/index',
                {
                    NYlakes: result[0],
                    NJlakes: result[1]
                }
            );
        
        };
        
        //map function
        function getData(item,callback){
        try{
            var a = wait.forMethod(item.coll,'find',item.filter);
            var b = wait.forMethod(a,'toArray');
            callback (null, b);
        } catch(err){
            callback(err);
        }
        

        我不熟悉 mongo,所以你可能需要调整调用。

        【讨论】:

          【解决方案5】:

          这似乎是使用 await 的最少代码行数:

          var async = require("async"); //include async module
          ...
          async function getData() { //make sure to use async function
            var NYlakes = await db.collection('lakes').find(filterNY); //can append additional logic after the find() 
            var NJlakes = await db.collection('lakes').find(filterNJ);
          
            res.json({"NYLakes": NYLakes, "NJLakes": NJLakes}); //render response
          }
          
          getData();
          

          旁注:在这种情况下,await 充当Promise.all(),请注意不要滥用 await 功能。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-03-26
            • 1970-01-01
            • 2023-03-18
            • 1970-01-01
            • 1970-01-01
            • 2019-05-31
            相关资源
            最近更新 更多