簡體   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