2015-02-11 136 views
3

我正在處理大量惰性數據加載的應用程序。我想優先考慮基於'優先'參數的http請求。

這是使用它的概念。

$http.get(url, {params: query, priority: 1}) 

我在考慮使用$ http攔截器。類似的東西:

angular.module('myModule') 
.factory('httpPriorityInterceptor', function ($interval, $q) { 
    var requestStack = []; 

    return { 
     request: function (config) { 

      config.priority = config.priority || 3; 

      requestStack.push(config); 
      requestStack.sort(sortByPriority); 

      if (isFirstToGo(item)) return requestStack.pop(); 

      deferred = $q.defer(); 

      var intervalPromise = $interval(function(){ 

       if (isFirstToGo(item)) { 
        deferred.resolve(requestStack.pop()); 
        $interval.cancel(intervalPromise); 
       }; 

      }, 100); 

      return deferred.promise; 

     } 
    }; 
}); 

但我不能在這裏返回承諾。有任何想法嗎?

回答

5

您可以通過使用$http的超時屬性的做到這一點,同時使用requestresponseError回調保存並分別執行各自$http請求。

步驟:

  1. 懶洋洋地注入request回調過程中的$http服務,這將是獲得$http服務,因爲在工廠的功能注入它會導致循環依賴的唯一途徑。

  2. 確定是否已在request回調中傳遞的配置已處理。如果尚未處理,請將該配置添加到請求堆棧中並按優先級進行排序。在配置對象的超時屬性中添加已解析的承諾,以取消當前的$http請求。最後返回配置對象。

  3. 一旦$http請求已被取消,趕上它在responseError回調。如果請求堆棧中有項目,請彈出第一個項目(config)並使用延遲加載的$http服務調用它。最後使用回調提供的拒絕參數返回拒絕的承諾。

DEMO

angular.module('demo', []) 

    .config(function($httpProvider) { 
    $httpProvider.interceptors.push('httpPriorityInterceptor'); 
    }) 

    .factory('httpPriorityInterceptor', function($q, $injector) { 


    var requestStack = [], // request stack 
     $http = null; // http service to be lazy loaded 

    return { 
     request: request, // request callback 
     responseError: responseError // responseError callback 
    }; 

    // comparison function to sort request stack priority 
    function sort(config1, config2) { 
     return config1.priority < config2.priority; 
    } 

    function request(config) { 

     // Lazy load $http service 
     if(!$http) { 
     $http = $injector.get('$http'); 
     } 

     // check if configuration has not been requested 
     if(!config.hasBeenRequested) { 

     // set indicator that configuration has been requested 
     config.hasBeenRequested = true; 

     // set default priority if not present 
     config.priority = config.priority || 3; 

     // add a copy of the configuration 
     // to prevent it from copying the timeout property 
     requestStack.push(angular.copy(config)); 

     // sort each configuration by priority 
     requestStack = requestStack.sort(sort); 

     // cancel request by adding a resolved promise 
     config.timeout = $q.when(); 
     } 

     // return config 
     return config; 
    } 


    function responseError(rejection) { 

     // check if there are requests to be processed 
     if(requestStack.length > 0) { 

     // pop the top most priority 
     var config = requestStack.pop(); 
     console.log(config); 

     // process the configuration 
     $http(config); 
     } 

     // return rejected request 
     return $q.reject(rejection); 
    } 

    }) 

    .run(function($http) { 

    // create http request 
    var createRequest = function(priority) { 
     $http.get('/priority/' + priority, {priority: priority}); 
    }; 

    createRequest(3); 
    createRequest(1); 
    createRequest(4); 
    createRequest(2); 

    }); 

要確保每個請求以正確的順序被調用,您可以檢查在控制檯選項卡中的日誌或在網絡選項卡中的請求。

更新:

如果你希望你的請求,以便調用(當第一個要求必須完成下一個請求調用之前),那麼你可以調整我的responseError回調的解決方案是這樣的:

DEMO

function responseError(rejection) { 

    // check if there are requests to be processed 
    if(requestStack.length > 0) { 

    requestStack.reduceRight(function(promise, config) { 
     return promise.finally(function() { 
     return $http(config); 
     }); 
    }, $q.when()); 

    requestStack.length = 0; 

    } 

    // return rejected request 
    return $q.reject(rejection); 
} 
+0

這是我正在尋找的解決方案類型。我會盡力實施它。謝謝! – krystiangw 2015-02-11 20:31:11

+0

ryeballar我喜歡你的答案,但你放在請求堆棧中的諾言一旦複製似乎不會執行先前承諾的代碼。真正的更優雅的解決方案是在您將請求放入堆棧的時候推遲執行,但我找不到一個好的方法來執行此操作。 – 2015-11-04 10:13:44

+0

你能提供一個等效的plnkr或者或者jsfiddle這種情況嗎? – ryeballar 2015-11-05 02:45:42

1

嘗試包你超時

var deferred = $q.defer(); 
     (function (_deferred){ 
     var intervalPromise = $interval(function(){ 

      if (isFirstToGo(item)) { 
       _defferred.resolve(requestStack.pop()); 
       $interval.cancel(intervalPromise); 
      }; 

     }, 100); 
     })(deferred); 
return deferred.promise; 

好像它過得好$間隔丟失。以及你的延期實例全局設置爲var之前。

+0

是的,你是對的工作。但正如我所說的,問題是我不能在這裏回覆諾言。它只接受'配置'對象。 – krystiangw 2015-02-11 10:26:35

+0

在$ http.get方法中稍微挖了一點,沒有看到在此設置優先級的機會。也許在調用$ http.get之前創建一個自己的服務來處理這個和優先級? 'service.httpGet(url,{priority:1});'? – graphefruit 2015-02-11 11:20:23

0

這不是正確的解決方案。您可以通過編寫自己的服務來實現此目的,以在調用http get之前優先考慮api調用隊列。

這不會對以下使用情況 Angular Http Priority