繁体   English   中英

解决Angular Service中的承诺

[英]Resolving a promise in Angular Service

我们有一个服务,叫它AccountService ,它公开了一个名为getAccounts(customerId)的方法。

在其实现中,它要做的就是触发$ http GET请求,并向调用控制器返回承诺,一旦解决,它将把返回的帐户数组放入控制器范围内。

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

// 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,但事实并非如此。

预先感谢您的输入

要回答您的原始问题:

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

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

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

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

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

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

您可以这样设计:一旦$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
  };
}]);

这是工作中的plnkr 您可以打开控制台,然后单击“ GetData”按钮。 您将看到它的日志第一次get from $http ,在随后的调用中,它的日志get from cachedData

一种方法是重用对象并用数据填充它。 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代码来解决,而不是更改其工作方式。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM