问题循环中的promises问题

时间:2016-04-18 08:15:07

标签: javascript for-loop asynchronous callback promise

我遇到的情况让我有点生气。

所以情况如下:

module.exports = {
  
  generation: function (req, res) {

    // Let's firstly fetch all the products from the productTmp Table
    function fetchProductsTmp (){
      ProductsTmp.find().then(function (products) {
        return Promise.all(products.map (function (row){
           Service.importProcess(row);
        }));
      });
    }
    fetchProductsTmp();
  }

在这里,我只需调用我的模型ProductsTmp来获取我的数据并遍历调用importProcess的行。

importProcess:

importProcess: function (product) {

    async.series([
      function (callback) {
        return SousFamille.findOne({name: product.sous_famille}).then(function (sf) {
          console.log('1');
          if (!sf) {
            return SousFamille.create({name: product.sous_famille}).then(function (_sf)             {
              console.log('2');
              callback(null, _sf.sf_id);
            });
          } else {
              callback(null, sf.sf_id);
          }
        });
      },
      function (callback){
        console.log('3');
      },
    ], function(err, results){
      if(err) return res.send({message: "Error"});

    });
      
}

所以我得到了我的控制台日志: 1 1 1 2 3 2 3 2 3

我想要获得的是1 2 3 1 2 3 1 2 3这样每个函数在调用下一个函数之前等待承诺完成。

2 个答案:

答案 0 :(得分:1)

在第一部分的generation功能中,替换

return Promise.all(products.map (function (row){
    Service.importProcess(row);
}));

var results = [],
    pushResult = id => results.push(id);
return products.reduce(function(prev, row){//Go through all the products
    //Take the previous promise, and schedule next call to Service.importProcess to be
    //made after the previous promise has been resolved
    return prev.then(function(){
        return Service.importProcess(row).then(pushResult);
    });
}, Promise.resolve())
.then(() => results);

您还需要从importProcess返回承诺才能生效。抛弃整个async.series的东西,做一些像

这样的事情
return new Promise(function(resolve, reject){
     ...
     resolve(sf.sf_id); //instead of the callback(null, sf.sf_id)
     ...
});

更新:这会强制对Service.importProcess的调用是顺序调用而不是并发调用,这会影响对generation的调用的整体性能。但我想你有比这些顺序console.log更有可靠的理由。

答案 1 :(得分:0)

抱歉,无法帮助在ES6中做到这一点,基本上可以将事情简化为单行,就像Bergi所说,async是多余的(使用Bluebird Promise Library):

importProcess: product => 
                  SousFamille.findOne({name: product.sous_famille})
                    .then(sf =>  sf? sf.sf_id : SousFamille.create({name: product.sous_famille}).then(_sf => _sf.sf_id))

// the other module
module.exports = {

  generation: (req, res) => ProductsTmp.find()
                              .then(products => Promise.mapSeries(products, Service.importProcess.bind(Service)) )
                              .then(ids => res.send({ids}))
                              .catch(error => res.send({message: 'Error'}))
  }

也像noppa所说,你的问题是return中缺少Service.importProcess(row),ES5中的代码相同:

module.exports = {

  generation: function (req, res) {

      ProductsTmp.find()
        .then(function (products) {
          return Promise.mapSeries(products, Service.importProcess.bind(Service)) );
      }).then(function(ids){
        res.send({ids: ids});
      }).catch(function(error){
        res.send({message: 'Error'});
      })
}


importProcess: function (product) {

    return SousFamille.findOne({name: product.sous_famille})
      .then(function (sf) {
          if (sf) return sf.sf_id;
          return SousFamille.create({name: product.sous_famille})
                    .then(function (_sf){ return _sf.sf_id});
    });      
}