2014-09-27 70 views
1

我想建立一個服務或工廠,允許我通過一些API調用加載日期。 大部分數據都需要重新使用,所以基本上我只想進行一次API調用,下一次我需要這些數據時,它應該只是返回它。Angular.js服務/工廠與可重複使用的數據

現在每當我進行API調用,並且在完成之前,我都會進行相同的調用,我希望第二個調用等待,直到第一個調用完成。

本質上講,當我這樣做:

dataService.getMenu() // Make API call 
dataService.getMenu() // Wait for the first API call to be completed and return that data 

// Somewhere else 
dataService.getMenu() // Return data as API call was already made 

我廠是這樣的:

(function() { 
    var app = angular.module('dataService', []); 
    app.factory('dataService', ['$http', '$q', function($http, $q) { 
     var links = [], 
      jobs = []; 

     return { 
      getMenu: function() { 
       var deferred = $q.defer(); 

       console.log(links); 

       if(links.length > 0) { 
        deferred.resolve(links); 
       } else { 
        $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { 
         links = data; 

         deferred.resolve(data); 
        }) 
       } 

       return deferred.promise; 
      } 
     } 
    }]) 
})(); 

回答

2

只是移動的使用getMenu功能外延遲報關,進廠

app.factory('dataService', ['$http', '$q', function($http, $q) { 
     var links = [], 
      jobs = [], 
      deferredMenu = $q.defer(); 

現在在你的getMenu調用中使用deferredMenu承諾。

getMenu: function() { 
       if(links.length > 0) { 
        deferredMenu.resolve(links); 
       } else { 
        $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { 
         links = data; 

         deferredMenu.resolve(data); 
        }) 
       } 

       return deferredMenu.promise; 
} 
+0

我的想法是太多,但如果我叫'getMenu'快兩倍,它會執行一個'$ http.get()'2次,是否只需要添加一個額外的變量來查看是否已經創建了通話? – 2014-09-27 05:24:28

+0

我可能已經解釋了自己的錯誤,基本上在加載'$ http.get()'時會有幾秒鐘的時間,無論何時在此期間調用'getMenu()',我都不想進行相同的調用再次。 – 2014-09-27 05:26:37

+0

是的,這種方法第二個電話不會去。記住函數總是返回相同的延遲對象。對函數的第一次調用會創建延遲對象,所有其他對象都會得到相同的延遲。 – Chandermani 2014-09-27 07:33:54

0

您可以在$http服務使用cache配置的爲你做到這一點。由於$http caching文檔中指出:

要啓用緩存,請求配置緩存屬性設置爲 真(使用默認緩存),或自定義緩存對象( $ cacheFactory建)。啓用緩存時,$ http將來自服務器的響應 存儲在指定的緩存中。 相同的請求 由下一次,響應從緩存提供,而不發送 請求到服務器

請注意,即使響應從緩存提供,交貨 的數據以與實際請求相同的方式異步。

如果是,應該是 使用相同的緩存中的同一個URL多個GET請求,但緩存未填充的是,只有 一個請求到服務器將被製成,剩餘的請求將 使用來自第一請求的響應來滿足

以上聲明符合您的問題中陳述的要求。此外,我省略了$q服務,因爲$http方法已經提供了您需要的承諾,您只需使用then()$q服務方法在您的響應中擁有數據對象。

(function() { 
    var app = angular.module('dataService', []); 
    app.factory('dataService', ['$http', function($http) { 
     return { 
      getMenu: function() { 
       return $http.get('../server/api.php?ajax=true&action=getCats', {cache: true}) 
        .then(function(response) { 
        return response.data; 
        }); 
      } 
     }; 
    }]) 
})(); 
+0

謝謝,我之前看過緩存中的構建,之所以我沒有這樣做,是因爲我可能需要修改返回數據,我希望將這個邏輯保留在我的服務中。 – 2014-09-27 07:36:03

+0

您仍然可以修改'.then()'方法中的數據。 – ryeballar 2014-09-27 07:37:05

0

我知道我有點晚回答這個問題,但我面臨同樣的問題,經過大量研究後提出了一個解決方案。

達到上述要求的方法是使用呼叫排隊

以下是相同的步驟:

  1. 對於每個調用,創建一個承諾,承諾加入到隊列中。每次呼叫返回defer.promise
  2. 對於隊列中的第一項,調用一個函數,它將帶上您的api並在API響應中設置一個參數,如IsDataPresent = true(最初爲false)。
  3. 解決第一個調用的承諾並將接收到的數據設置爲局部變量。執行隊列中下一個呼叫的功能,但首先檢查IsDataPresent=== true,如果爲true,則使用局部變量的數據解析下一個呼叫的承諾。

請參見下面的代碼是一樣的:

app.factory('dataService', ['$http', '$q', function($http, $q) { 
    var links = '', 
     jobs = [], 
     isDataAlreadyPresent = false; 

    var getMenuCall = function() { 
     var call = jobs[0]; // Retrieve first promise from the queue 
     if (isDataAlreadyPresent) { // This will be false for first call and true for all other 
      call.defer.resolve(links); 
     } else { 
      $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { //Get API data 
       isDataAlreadyPresent = true; //Set parameter to true 
       links = data; // Set local variable to received data 
       call.defer.resolve.resolve(data); // Resolve the first promise 
       jobs.shift(); // Remove first item from the queue 
       if (jobs.length > 0) { 
        getMenuCall(); // Execute the function for next call's promise in the queue. This time isDataAlreadyPresent== true will be true so it's promise will be resolved by the links data thus avoiding extra call. 
       } 
      }); 
     } 

     return deferredMenu.promise; 
    }; 

    return { 
     getMenu: function() { 
      var defer = $q.defer(); // Create promise for the call 
      jobs.push({ 
       defer: defer // Push each call's promise to the queue 
      }); 
      if (jobs.length === 1) { // For the first call make call above function which will make API call 
       getMenuCall(); 
      } 
      return defer.promise; // Defer promise for the time being 
     } 
    } 
}]);