解决Angular Service中的承诺

时间:2017-01-19 03:22:37

标签: javascript angularjs

我们有一项服务,让我们称之为AccountService,它会公开一种名为getAccounts(customerId)的方法。

在它的实现中,它所做的只是启动一个$ http GET请求并向调用控制器返回一个promise,它将一旦解析就将返回的帐户数组放入控制器范围。

在简化视图中,所有内容如下所示:

// The service
.factory('AccountService', ['$http', function($http) {
    var _getAccounts = function(customerId) {
        var request = {
            'method': 'GET',
            'url': 'http://localhost:8081/accounts/' + customerId
        };
        return $(request);
    };

    return {
        getAccounts: _getAccounts
    };
}]);

// Inside the conntroller
AccountService.getAccounts($scope.customerId)
    .then(function(response) {
        $scope.accounts = response.data;
    });

因此,一旦承诺将解决,控制器范围将填充帐户列表。

请注意,我保持上面的代码尽可能简单,让你知道我的问题是什么,但实际上它将是处理异常的代码,观察者刷新等等。一切正常。

我的问题是,这个AccountService是从很多控制器中使用的,并且承诺解决所有这些看起来不仅重复所有这些锅炉板解析器代码而且还使单元测试变得复杂,因为我不得不r / test每个控制器测试中的成功和异常情况。

所以我的问题是: 有没有一种很好的方法来解决服务中的承诺并将响应返回给控制器,而不是承诺?

请注意我是Angular和JS的初学者,所以如果我的问题看起来很幼稚,请保持温柔。我有大量的java经验,我的思想似乎像所有地方的java一样,可能不是这样。

提前感谢您的输入

3 个答案:

答案 0 :(得分:1)

回答你原来的问题:

  

有没有一种很好的方法来解决服务中的承诺并将响应返回给控制器,而不是承诺?

在我看来,不,没有。它归结为异步调用的工作方式 - 您要么传递回调(并且该方法不返回任何内容),要么您不传递回调,并且该方法返回将被通知的对象(承诺)。可能有一些解决方法,但我认为它不会比那更好。

部分减少样板的一种方法是在服务中使用catch,然后返回由它返回的promise

考虑以下极其简化的示例:

angular.module('myApp')
  .factory('NetworkRequests', [
    function() {
      var _getData = function() {
        var promise = new Promise((resolve, reject) => {
          var a = true,
              data = ['a', 'b', 'c'];

          if (a) {
            resolve(data);
          } else {
            reject('Rejection reason: ...');  
          }
        });

        return promise.catch((error) => {
          // Notify some error handling service etc.
          console.log(error);
          return [];
        });
      };

      return {
        getData: _getData
      };  
    }
  ]);    

promise变量将是您的http请求的结果。您应该在catch函数中返回一些在控制器上下文中有意义的数据(例如空数组)。然后您不必为控制器中的错误处理而烦恼:

angular.module('myApp')
  .controller('DataController', ['NetworkRequests',
    function(NetworkRequests) {
      NetworkRequests.getData().then((data) => {
        this.data = data;
      });
    }
  ]);

同样,这并不能解决完整的问题,但至少可以将错误处理部分封装在服务中。

答案 1 :(得分:0)

您可以这样设计:一旦您的$http完成获取数据,将其存储为工厂变量(稍微缓存),并为后续工厂调用,检查缓存是否具有此类数据。如果是,则返回缓存数据,否则调用$http调用。

以下是代码:

.factory('AccountService', ['$http', '$q', function($http, $q) {
  var cachedData = null;
  var defered = $q.defer(); //create our own defered object
  var _getAccounts = function(customerId) {
    if (cachedData !== null) {
      console.log('get from cachedData')
      defered.resolve(cachedData); // resolve it so that the data is passed outside
      return defered.promise; //return your own promise if cached data is found
    } else {
      var request = {
        'method': 'GET',
        'url': 'mockdata.json'
      };
      return $http(request).then((response) => { //return a normal $http promise if it is not.
        console.log('get from $http');
        cachedData = response.data;
        return cachedData;
      });
    }
  };

  return {
    getAccounts: _getAccounts
  };
}]);

这是working plnkr。您可以打开控制台,然后单击GetData按钮。您将看到它第一次记录get from $http,在后续调用时记录get from cachedData

答案 2 :(得分:0)

一种方法是重用一个对象并用数据填充它。它由ngResource使用。

类似

var data = [];

function getAccounts(customerId) {
    var promise = $http(...).then((response) => {
      Object.assign(promise.data, response.data)
    });

    promise.data = [];

    return promise;
};

数据可以绑定为$scope.accounts = AccountService.getAccounts(...).data。明显的缺点是有大量未加载的内容。

另一种方式是你提到过的那种方式。它被最频繁地使用。如果控制器中的WET代码有问题,应该通过消除类继承的WET代码来处理它,而不是通过改变它的工作方式来处理。

另一种方法是推荐的方式。使用路由器和路由/状态解析器消除了对异步加载数据的需要。解析器中解析的数据作为数组注入到路径模板中。