AngularJS:多次调用服务函数返回单独的promise

时间:2014-08-10 10:44:37

标签: angularjs asynchronous promise factory

好的,这是上一个问题的重新发布,我对过度简化代码感到困惑......

我有一个angularjs工厂函数,它查询一个解析数据库,并返回一个promise ...到目前为止,非常简单...但是,我的控制器有一个foreach循环,它调用每个id的每一个函数,这意味着然后同时解决。然而,决心不起作用,我所得到的似乎是api调用的混乱。

这是我的代码的简化版本......

app.factory('MyFactory', function($http, $q ) {
  return {
    testFunction: function (ingredientList) {

      var deferred = $q.defer()

      ingredientIndex=-1

      var regulariseIngredient=function() {

        ingredientIndex++

        if (ingredientIndex>ingredientList.length-1) {
          deferred.resolve(ingredientList);
          return
        }

        (new Parse.Query("Ingredient"))
        .equalTo("type", ingredientList[ingredientIndex].type)
        .equalTo("name", ingredientList[ingredientIndex].name)
        .include("parent")
        .find().then(function(result) {
          result=result[0]
          if(result.get("parent")) result=result.get("parent")                        
          ingredientList[ingredientIndex]=result
          regulariseIngredient();
         })                               

       }
       regulariseIngredient()
       return deferred.promise
    }
  }
}

app.controller('TestController', function($scope,MyFactory) {
  recipes = [ An array of objects, each with an ingredients array]
  angular.forEach(recipes, function(recipe) { 
    MyFactory.testFunction(recipe.ingredients).then(function(result) {
      console.log(result)
    })
  })
} 

关于服务实际做什么的更多信息......

我的应用程序有一系列食谱和一系列成分。每种成分都有一个'父母',用'合理化'版本代替它。测试函数遍历每个成分,编译结果,然后返回一组替代成分。控制器正在循环配方,这样我就可以检查所有配方的原料。

2 个答案:

答案 0 :(得分:2)

@MarkPeace,看来你已经击中反模式"非法" here标题为" The Collection Kerfuffle"。

espoused模式(用于在项目数组的每个成员上并行执行异步任务)是:

function workMyCollection(arr) {
    return q.all(arr.map(function(item) {
        return doSomethingAsync(item);
    }));
}

在您的情况下,doSomethingAsync()(new Parse.Query("Ingredient"))...find();

完整的,代码将是:

app.factory('MyFactory', function($http, $q) {
    return {
        testFunction: function (ingredientList) {
            return $q.all(ingredientList.map(function(item) {
                return (new Parse.Query("Ingredient"))
                    .equalTo("type", item.type)
                    .equalTo("name", item.name)
                    .include("parent")
                    .find().then(function(result) {
                        return result[0].get("parent") || result[0];//phew, that's a bit more concise
                    });
            }));
        }
    }
}); 
因此,

testFunction()将返回一系列结果的承诺(无论它们是什么)。

在控制器中,您可以再次使用相同的模式来遍历配方:

app.controller('TestController', function($scope, MyFactory) {
    var recipes = [ An array of objects, each with an ingredients array ];
    $q.all(recipes.map(function(recipe) {
        return MyFactory.testFunction(recipe.ingredients).then(function(results) {
            console.dir(results);
            return results;
        });
    })).then(function(arr) {
        //all done-and-dusted
        //If everything has been successful, arr is an array of arrays of results
        //Do awesome stuff with the data.
    })['catch'](function(err) {
        //report/handle error here
    });
});

当然,您在控制器中的确切程度以及工厂(或工厂)的数量取决于您。就个人而言,我会选择让控制器更简单。

答案 1 :(得分:1)

使用$ q.all()将一个promises数组作为参数并返回一个新的promise:

  • 解决了是否已解决数组的所有承诺
  • 如果数组的任何承诺被拒绝则被拒绝

来自doc:

  

返回将使用值的数组/散列解析的单个promise,每个值对应于promises数组/散列中相同索引/键的promise。如果任何承诺通过拒绝得到解决,则此结果承诺将被拒绝并具有相同的拒绝价值。

对于循环,使用Array.forEach更清晰:

testFunction:     function (ingredientList) {
    var promiseArray = [];

    ingredientList.forEach(function (ingredient) {
        var promise = (new Parse.Query("Ingredient"))
                .equalTo("type", ingredient.type)
                .equalTo("name", ingredient.name)
                .include("parent")
                .find().then(function(result) {
                    result = result[0]
                    if(result.get("parent")) {
                        result = result.get("parent");
                    }
                    return result;
                });
        promiseArray.push(promise);
    });

    return $q.all(promiseArray);
}