Angular $ q promises和non-linear chaining

时间:2016-09-23 02:10:47

标签: javascript angularjs asynchronous promise angular-promise

大家好。我撞到了一堵砖墙。我正在尝试研究Angular异步方法调用,保证对象并实现特定问题的解决方案。经过几个小时的反复试验和重构我的代码后,我确信在这一点上答案就在我的鼻子底下,但是我看不到它。

我有一个场景,我必须使用$ http来进行后端服务调用,这会产生一个承诺。在promise的结果中,我将收到一个数据对象,其中包含一个字符串属性和一个包含100个ID的数组。此后端服务一次只能提供100个ID的有效负载,因此我必须使x个$ http呼叫到达列表的末尾,这是由此后端服务提供的。据我所知,我必须在promise的.then()方法中评估$ http响应,如果字符串属性为'N',那么我知道我必须再次为另一批100个ID调用后端服务。在传递完所有ID之后,字符串将包含一个“Y”,表示已经发送了该结尾,并且不再调用。这个计划没有需要评论,我知道这是相当蹩脚的,不幸的是我无法控制。 ;-)

我所研究的关于承诺的一切似乎只是以线性的方式说明了承诺的链接,如果需要更多的异步调用。无论承诺链是嵌套还是扁平,似乎我只能进行“已知”或固定数量的顺序调用,例如承诺1到Promise2,到Promise3等等。请原谅我在这里承诺的有限经验。

这是一个代码示例,你可以看到我被困在这里,向下嵌套。我不能只是“希望”在3或5或10个电话后我将获得所有ID。我需要动态评估每个呼叫的结果并再次呼叫。

这是调用$ http

的Angular服务
(function() {
'use strict';

  angular
      .module('myapp')
      .factory('IDservice', IDservice);

  function IDservice($http) {

    var service = {
    model: {
        error: {
          state: false,
          message: ''
        },
        ids: [],
        end: '',
      },
      GetIDs: function() {
        var request = 'some params...';
        var url = 'the url...';
        return $http.post(url, request).then(reqComplete).catch(reqFailed);

        function reqComplete(response) {
          service.model.ids = response.data.idList;
          service.model.end = response.data.end;
          return service.model;
        }

        function getIntradayMsrFailed(error) {
          service.model.error.state = true;
          service.model.error.message = error;
          return service.model;
        }

      }
    };
    return service;
  }

})();

这是调用Angular服务的Controller,它最终驱动相应视图上的一些UI元素:

(function() {
  'use strict';

  angular
    .module('myapp')
    .controller('AvailableIDsController', AvailableIDsController);

  function AvailableIDsController($scope, $location, $timeout, IDservice) {
    var vm = this;
    vm.completeIDList = [];

    activate();    

    function activate() {
        IDservice.GetIDs().then(function(response){
          vm.completeIDList = response.ids;
          console.log('promise1 end');
            if(response.end === 'N'){
              IDservice.GetIDs().then(function(response){
                angular.forEach(response.ids,function(nextID){
                  vm.comleteIDList.push(nextID);
                 });
                console.log('promise2 end'); 
                if(response.end === 'N'){
                  IDservice.GetIDs().then(function(response){
                    angular.forEach(response.ids,function(nextID){
                      vm.comleteIDList.push(nextID);
                    });
                    console.log('promise3 end');
                  });   
                }
             });    
           }
        });         

     console.log('mainthread end');
     }
}
})();

你可以看到它在哪里......它非常非常难看。

我需要一种方法,在activate()方法中,调用一个方法,该方法将负责调用服务调用并将结果返回到activate()。现在,仍然在activate()方法中,评估结果并确定是否再次调用等等。我遇到的情况是,一旦主处理线程完成,你就会在第一个承诺中留下程序控制。从那里,你可以执行另一个承诺等,然后沿着兔子洞走下去。一旦我陷入这个陷阱,一切都将失去。显然,我没有做到这一点。我错过了其他一些简单的拼图。任何建议都会非常感激!

1 个答案:

答案 0 :(得分:2)

你正在寻找普通的旧递归:

function AvailableIDsController($scope, $location, $timeout, IDservice) {
    var vm = this;
    vm.completeIDList = [];
    return activate(0);    

    function activate(i) {
        return IDservice.GetIDs().then(function(response) {
            [].push.apply(vm.completeIDList, response.ids); // simpler than the loop
            console.log('promise'+i+' end');
            if (response.end === 'N'){
                return activate(i+1);
            }
        });
     }
}

不要忘记return,以便将承诺链在一起,您可以等待请求的结束。