【问题标题】:AngularJS : Multiple calls to a service function returning separate promisesAngularJS:对服务函数的多次调用返回单独的承诺
【发布时间】:2014-10-03 08:49:22
【问题描述】:

好的,这是对先前问题的重新发布,我因过度简化代码而感到困惑......

我有一个 angularjs 工厂函数,它查询解析数据库,并返回一个承诺......到目前为止,非常简单......但是,我的控制器有一个 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)
    })
  })
} 

有关该服务实际作用的更多信息...

我的应用包含一组食谱和一组配料。每种成分都有一个“父母”,可以在“合理化”版本中替代它。测试函数循环遍历每个成分,编译结果,然后返回替换成分的集合。控制器正在循环遍历食谱,以便我可以检查所有食谱的手头成分。

【问题讨论】:

  • 没有列表循环,不会改变您的ingredientIndex。如果它确实发生了变化,您不会根据结果解决

标签: angularjs asynchronous promise factory


【解决方案1】:

@MarkPeace,您似乎在“The Collection Kerfuffle”标题下点击了“非法”here 的反模式。

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

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
    });
});

当然,您在控制器中执行多少操作以及在工厂(或多个工厂)执行多少操作取决于您。就个人而言,我会选择让控制器稍微简单一些。

【讨论】:

  • 谢谢你,它很有用 - 感觉它朝着正确的方向前进(并帮助我更好地理解角度的简洁性)。我对“传播”有点问题......它似乎正在产生这个异常:未捕获的类型错误:对象#没有方法“传播”......我还缺少什么吗?
  • 我承认我并不是真正的 Angular 人,只是一个全面了解 javascript 的人。我的理解是.spread 是可用的,但从你所说的来看,我错了。 @BenjaminGruenbaum 选择的答案 here 允许您使用 Bluebird Promise,并与 Angular 的摘要循环完全集成。那么你肯定会有一个.spread 方法。 Benjamin 究竟是如何将 Bluebird 集成到 Angular 中的,这谁都猜不透——而且只有 7 行!
  • 如果您在这里报告 Bluebird 集成有效,那么我将编辑答案。
  • 万岁 - 工作!感谢您的帮助(附注:Bluebird 集成不起作用 - 尽管我没有坚持使用它,而且正如您所说,我不需要它)
【解决方案2】:

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

  • 如果数组的所有承诺都已解决,则已解决
  • 如果数组的任何承诺被拒绝,则被拒绝

来自文档:

返回一个单一的promise,它将用一个数组/哈希值解析,每个值对应于promise数组/哈希中相同索引/键的promise。如果任何一个 Promise 以一个拒绝的方式解决,这个产生的 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);
}

【讨论】:

  • 我已经尝试过这样做,在传回原始延迟承诺对象之前解决了一个承诺调用数组,但是当多次调用时,我仍然会收到混乱的响应
猜你喜欢
  • 2014-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-24
  • 2015-01-30
  • 2014-10-05
相关资源
最近更新 更多