在 AngularJS 1.4 中的另一个 return 之后多次调用 $resource

Make multiple calls to $resource after the return of another one in AngularJS 1.4

我目前正在尝试寻找一种最佳实践,以便在第一个 return 的函数中对 $resource 进行多次调用。

考虑以下模式:我们有一个作者数据库,其中有书籍,有多个标签(科幻、惊悚、恐怖……)。

angular.module('myApp',[ngResource])
    .controller('myCtrl',['$scope', function($scope){
        $scope.results = [];

        apiService.Authors.getAll().$promise                                         //return array of authors
            .then(function(authors){
                $scope.results.push(authors);
                angular.forEach(authors,function(akey,author){
                    apiService.BooksBy.getAll({authorID:author.id}).$promise         //return array of books
                        .then(function(books){
                            $scope.results[akey].books = [];
                            $scope.results[akey].books.push(books);
                            angular.forEach(books,function(bkey,book){
                                apiService.Tags.getAll({bookID:book.id}).$promise    //return array of tags
                                    .then(function(tags){
                                        $scope.results[akey].book[bkey].tags = [];
                                        $scope.results[akey].book[bkey].tags.push(tags);
                                    });
                            });
                        });
                });
            });
    }]);

这是进行多次嵌套调用的唯一方法吗?

我在想一些更扁平化和更具可读性的东西,比如链式承诺的解决方法。

欢迎提出任何想法,因为我找不到更好的方法。

此致!

您可以使用$q 拨打多个电话。它是调用多个调用的最佳方式,如果所有调用都是 return promises

,它就会结束

如果您根据之前的结果调用多个调用,那么这是执行此操作的最佳方法。

function httpCall()
{
     var promises=[];
      promises.push(yourhttpCall1())
     return $q.all(promises);
}

$q.all([httpCall]).then(function(result){});

这是一个可能的解决方案:如果你使用 $q.all,但是对于你推送到数组的每个承诺,你添加一点 'sub'-.then,你可以包装结果每个人 api 调用并将其与您需要的密钥(或其他任何内容)一起传递给下一个 .then-处理程序。

我已尝试相当接近地反映您的原始代码示例,并进行了一些更改:

  • 我创建了一个小的 mockApiService,但为了简单起见,我捏造了几个调用
  • 我已经用命名函数替换了内联 .then-处理程序,以更清楚地证明我们已经展平链
  • 我正在使用 angular.extend 更新作用域上的 json 结构,该结构显示在 <pre> 标记内。您的原始代码示例是用数组做一些事情,但我不确定它是否会按预期工作 - 我使用 angular.extend 是为了近似我所想的。
  • 在我的 applyBooksGetTags 函数中,我们试图将 akeybkey 与结果一起传递。然而,akey 实际上是从之前的结果中获得的——作为 result.akey 访问。这存在于我们用来编译 promise 数组的 angular.forEach 循环之外的范围内——当 forEach 函数执行时,result.akey 将被外部 [=27] 更改=] 循环。出于这个原因,我将对 forEach 的调用包装在一个自执行函数中 - 一个闭包,以确保 result.akey 的值在 forEach 执行其操作时得到维护。
  • 原始示例中的 angular.forEach 调用可能交换了(值,键)参数的顺序 - 因此在下面的代码片段中交换回来了
  • 有很多 } //end blah blah 评论...这主要是因为代码片段编辑器 'tidy' 功能使代码难以遵循;抱歉!

(function() {
  "use strict";

  angular.module('myApp', [])
    .controller('myCtrl', ['$scope', '$q', 'apiService', MyCtrl])
    .service('apiService', ['$timeout', mockApiService]);

  function MyCtrl($scope, $q, apiService) {
      $scope.results = {};

      apiService.Authors.getAll().$promise
        .then(applyAuthorsGetBooks)
        .then(applyBooksGetTags)
        .then(applyTags);

      function applyAuthorsGetBooks(authors) {
          var promises = [];
          angular.extend($scope.results, authors);

          angular.forEach(authors, function(author, akey) {
            promises.push(apiService.BooksBy.getAll({
                authorID: author.id
              }).$promise
              .then(function(books) {
                return $q.when({ // < MAGIC HERE: return the books, but also the aKey
                  books: books,
                  akey: akey
                }); //end return
              }) //end then
            ); //end push
          }); //end foreach

          return $q.all(promises);
        } // end authorsGetBooks

      function applyBooksGetTags(results) {
          var promises = [];

          for (var i = 0; i < results.length; i++) {
            var result = results[i];
            $scope.results[result.akey].books = angular.extend({}, result.books);

            (function(akey) { //anon func to wrap the current value of akey in a closure, so when inner loop accesses it hasn't been changed by outer loop
              angular.forEach(result.books, function(book, bkey) {
                promises.push(apiService.Tags.getAll({
                    bookID: book.id
                  }).$promise
                  .then(function(tags) {

                    return $q.when({ // < MAGIC HERE, again: return the tags, but also the bkey and akey
                      tags: tags,
                      akey: akey,
                      bkey: bkey
                    }); //end return
                  }) //end then
                ); //end push

              }); //end foreach
            })(result.akey) //pass our current akey value to our anon func

          } //end for
          return $q.all(promises);
        } //end booksGetTags

      function applyTags(results) {
        for (var i = 0; i < results.length; i++) {
          var result = results[i];
          $scope.results[result.akey].books[result.bkey].tags = angular.extend({}, result.tags);
        } //end for
      }

    } //end MyCtrl

  function mockApiService($timeout) {
      function _simulateResource(data) {
          return {
            $promise: $timeout(function timeoutHandler() {
                return data;
              }, 1000) //end $timeout
          }; //end return
        } //end _simulateResource()

      return {
        Authors: {
          getAll: function authorsGetAll() {
              return _simulateResource({
                Author1: {
                  id: 'Author 1'
                },
                Author2: {
                  id: 'Author 2'
                }
              }); //end return
            } //end getAll
        }, //end Authors
        BooksBy: {
          getAll: function booksByGetAll(params) {
              var authorId = params.authorID;
              return _simulateResource({
                Book1: {
                  id: 'Book 1 by ' + authorId
                },
                Book2: {
                  id: 'Book 2 by ' + authorId
                }
              }); //end return
            } //end getAll
        }, //end BooksBy
        Tags: {
          getAll: function tagsGetAll(params) {
              var bookId = params.bookID;
              return _simulateResource({
                Rom: {
                  id: 'Rom'
                },
                Zom: {
                  id: 'Zom'
                },
                Com: {
                  id: 'Com'
                }
              }); //end return
            } //end getAll
        } //end Tags
      }; //end return
    } // end MockService API
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<div ng-app="myApp">
  <pre ng-controller='myCtrl'>{{results|json}}</pre>
</div>