循环中的承诺问题

Issue with promises in a for loop

本文关键字:问题 承诺 循环      更新时间:2023-09-26

我遇到了一个让我有点抓狂的情况。

因此情况如下:

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 2 3,这样每个函数都要等待promise完成,然后再调用下一个。

在第一节的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更充分的理由这样做。

对不起,忍不住想在ES6中做这件事,基本上可以减少到单行,就像Bergi所说的,异步是多余的(使用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所说,你的问题是Service.importProcess(row)中缺少return,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});
    });      
}