2014-06-05 22 views
2

假設我目前的路線是/ books,並且我打了一個$ http電話來獲取所有想要顯示給用戶的書籍。通常情況下,調用會很快解決,並且書籍會重複進入DOM。當我們發生錯誤時(例如超時或沒有書籍返回),我們更新一個通用的全局視圖,它將覆蓋內容視圖並顯示一條消息,如「沒有可用的書」。通用視圖是通過CommonView.showLoading(),CommonView.showError(「沒有書籍可用」)和CommonView.hide()等方法通過服務處理的。

最近,我發現如果$ http不能很快解決,用戶可能會離開並轉到另一條路線(可能是/恐龍)。最終,當$ http最終解析或被拒絕時,將會發生顯示該公共全局視圖的promise調用,從而導致在不應該顯示錯誤視圖時顯示該錯誤視圖,並且錯誤對於用戶(即用戶處於/恐龍狀態,錯誤屏幕彈出「沒有書籍可用」)。

我已經看到,你可以用超時承諾取消$ http,但這仍然可能導致競爭條件(也許你在處理完resolve()或reject()之後調用cancel) 。我認爲必須檢查當前路線是否與$ http發起的路線相匹配會很麻煩。

看起來應該有一些標準的方法來銷燬路由變化或控制器的$ destroy方法上的$ http調用。我真的很想避免在我的巨大應用程序中添加很多條件。

+0

只是一個想法:你可以讓執行遠程調用的服務監聽路由改變事件。如果發生,由於特殊原因拒絕所有待處理的請求,例如' 「NAVIGATED_AWAY」'。您的用戶界面代碼將測試失敗的原因,如果是「NAVIGATED_AWAY」,則不會顯示該消息。 –

+0

我擔心你被承諾:) – gkalpak

回答

1

我找不到一個很好的方法來停止處理我的回調,如果它已經開始,但這裏是我製作的$ http封裝器,用於在路由更改後嘗試並阻止延遲迴調。它不會複製所有$ http方法,只是我需要的方法。我還沒有完全測試過它。我只驗證它能在正常情況下工作(標準調用的正常帶寬,即httpWrapper.get(url).success(cb).error(err))。你的旅費可能會改變。

angular.module('httpWrapper', []).provider('httpWrapper', function() { 
    this.$get = ['$rootScope','$http','$q', function($rootScope, $http, $q) { 
     var $httpWrapper = function(config) { 
     var deferred = $q.defer(); 
     var hasChangedRoute = false; 
     var canceler = $q.defer(); 
     var http = null; 
     var evListener = null; 
     var promise = deferred.promise; 

     if ((config || {}).timeout && typeof config.timeout === 'Object') { 
      // timeout promise already exists 
      canceler.promise = config.timeout; 
     } else { 
      angular.extend(config || {}, { 
       timeout: canceler.promise 
      }); 
     } 
     http = $http(config) 
      .success(function(data, status, headers, config) { 
       // only call back if we haven't changed routes 
       if (!hasChangedRoute) { 
        deferred.resolve({data:data, status:status, headers:headers, config:config}); 
       } 
      }) 
      .error(function(data, status, headers, config) { 
       // only call back if we haven't changed routes 
       if (!hasChangedRoute) { 
        deferred.reject({data:data, status:status, headers:headers, config:config}); 
       } 
      }); 

      evListener = $rootScope.$on('$locationChangeStart', function(scope, next, current) { 
       hasChangedRoute = true; 
       canceler.resolve('killing http'); 
       evListener(); // should unregister listener 
      }) 

      promise.success = function(fn) { 
       promise.then(function(response) { 
        fn(response.data, response.status, response.headers, config); 
       }); 
       return promise; 
      }; 
      promise.error = function(fn) { 
       promise.then(null, function(response) { 
        fn(response.data, response.status, response.headers, config); 
       }); 
       return promise; 
      } 
      return promise; 
     }; 

     angular.forEach(['get', 'delete', 'head', 'jsonp'], function(method) { 
      $httpWrapper[method] = function(url, config) { 
       return $httpWrapper(
        angular.extend(config || {}, { 
         method: method, 
         url: url 
        }) 
       ); 
      }; 
     }); 
     angular.forEach(['post', 'put'], function(method) { 
      $httpWrapper[method] = function(url, data, config) { 
       return $httpWrapper(
        angular.extend(config || {}, { 
         method: method, 
         url: url, 
         data: data 
        }) 
       ); 
      }; 
     }); 
     return $httpWrapper; 
    }]; 
});