2016-12-05 29 views
0

我正在使用Angular 1.5.8。我的應用程序中的視圖需要相同的3個Ajax請求的不同組合。一些視圖需要來自所有三個視圖的數據,其他視圖需要來自兩個或甚至一個端點的數據。鏈接多個可選的異步Ajax請求

我正在處理一個函數,該函數將管理這些數據的檢索,要求應用只調用一次端點。我希望根據需要將ajax請求稱爲,但僅在需要時。目前我已經創建了一個可行的功能,但似乎可以使用改進。

以下功能包含在$rootScope內。它使用fetchData()函數根據請求循環獲取請求。當數據被檢索時,它被存儲在全局變量$ rootScope.appData中,然後再次調用fetchData()。當檢索到所有數據時,延期承諾將被解析,並且數據將返回給控制器。

$rootScope.appData = {}; 

$rootScope.loadAppData = function(fetch) { 
    var deferred = $q.defer(); 

    function getUser() { 
    $http 
     .get('https://example.com/api/getUser') 
     .success(function(result){ 
     $rootScope.appData.currentUser = result; 
     fetchData(); 
     }); 
    } 

    function getPricing() { 
    $http 
     .get('https://example.com/api/getPricing') 
     .success(function(result) { 
     $rootScope.appData.pricing = result; 
     fetchData(); 
     }); 
    } 

    function getBilling() { 
    $http 
     .get('https://example.com/api/getBilling') 
     .success(function(result) { 
     $rootScope.appData.billing = result; 
     fetchData(); 
     }); 
    } 

    function fetchData() { 
    if (fetch.user && !$rootScope.appData.currentUser) { 
     getUser(); 
    } else if (fetch.pricing && !$rootScope.appData.pricing) { 
     getPricing(); 
    } else if (fetch.billing && !$rootScope.appData.billing) { 
     getBilling(); 
    } else { 
     deferred.resolve($rootScope.appData); 
    } 
    } 

    if ($rootScope.appData.currentUser && $rootScope.appData.pricing &&$rootScope.appData.billing) { 
    deferred.resolve($rootScope.appData); 
    } else { 
    fetchData(); 
    } 

    return deferred.promise; 
}; 

對象fetch作爲屬性提交,該對象顯示哪個ajax請求調用。一個例子調用$rootScope.loadAppData()只有用戶定價數據將被要求應該是這樣的:

$rootScope.loadAppData({user: true, pricing: true}).then(function(data){ 
    //execute view logic. 
}); 

我想知道:

  1. 如果這些功能的鏈接來完成不同? fetchData()功能是否足夠,或者這是執行此功能的奇怪方式?
  2. 有沒有辦法同時調用所有需要的Ajax請求,但在解決承諾之前等待所有需要的調用完成?
  3. $rootScope這樣的數據存儲是不尋常的嗎?

我知道這個函數目前沒有正確處理錯誤。這是我在使用此代碼段之前添加的功能,但與我的問題無關。

回答

2

而不是使用.success方法,使用.then方法和回報數據,其成功的處理程序:

function getUserPromise() { 
    var promise = $http 
     .get('https://example.com/api/getUser') 
     .then(function successHandler(result) { 
      //return data for chaining 
      return result.data; 
     }); 
    return promise; 
} 

使用服務,而不是$ rootScope:

app.service("myService", function($q, $http) { 

    this.loadAppData = function(fetchOptions) { 

     //Create first promise 
     var promise = $q.when({}); 

     //Chain from promise 
     var p2 = promise.then(function(appData) { 
      if (!fetchOptions.user) { 
       return appData; 
      } else { 
       var derivedPromise = getUserPromise() 
        .then(function(user) { 
        appData.user = user; 
        //return data for chaining 
        return appData; 
       }); 
       return derivedPromise; 
      ); 
     }); 

     //chain from p2 
     var p3 = p2.then(function(appData) { 
      if (!fetchOptions.pricing) { 
       return appData; 
      } else { 
       var derivedPromise = getPricingPromise() 
        .then(function(pricing) { 
        appData.pricing = pricing; 
        //return data for chaining 
        return appData; 
       }); 
       return derivedPromise; 
      ); 
     }); 
 //chain from p3 
     var p4 = p3.then(function(appData) { 
      if (!fetchOptions.billing) { 
       return appData; 
      } else { 
       var derivedPromise = getBillingPromise() 
        .then(function(user) { 
        appData.billing = billing; 
        //return data for chaining 
        return appData; 
       }); 
       return derivedPromise; 
      ); 
     }); 

     //return final promise 
     return p4; 
    } 
}); 

上面的示例爲空對象創建了一個承諾。然後它鏈接三個操作。每個操作都會檢查是否有必要執行提取操作。如果需要,執行提取並將結果附加到appData對象;如果不需要提取,則appData對象將傳遞給鏈中的下一個操作。

用法:

myService.loadAppData({user: true, pricing: true}) 
    .then(function(appData){ 
    //execute view logic. 
}).catch(functon rejectHandler(errorResponse) { 
    console.log(errorResponse); 
    throw errorResponse; 
}); 

如果任何讀取操作的失敗,在鏈後續操作將被跳過,最終拒絕處理程序將被調用。

由於調用承諾的.then方法會返回一個新的派生承諾,因此很容易創建一個承諾鏈。有可能創建任意長度的鏈,並且由於承諾可以通過另一個承諾來解決(這會進一步延遲其解決方案),因此可以在鏈中的任何時刻暫停/推遲解決承諾。這使得實現強大的API成爲可能。 - AngularJS $q Service API Reference - Chaining Promises

+0

感謝您的回答。在閱讀你的答案/做更多的研究之後,對我來說很明顯這個邏輯應該包含在一個服務中。我現在已經在我的項目中進行了改變。你是否知道一種方法可以並行而不是按順序執行所有必需的調用,然後在任何調用最長的時間結束時返回結果? – kravse

+1

使用'$ q.all'並行執行XHR。順序方法在下一個XHR依賴於前一個XHR的信息時非常有用。一個例子是在檢索用戶位置之後計算運輸成本。 – georgeawg

0

找到一個很好的方式來回答問題2在原來的文章。使用$q.all()允許承諾同時執行,一旦全部完成就解決,或者在其中一個失敗後立即失敗。感謝@georgeawg,我已將這一邏輯添加到服務中。這裏是我的重寫把這些代碼放入一個服務,並同時運行所有調用:

services.factory('appData', function($http, $q) { 
    var appData = {}; 
    var coreData = {}; 

    appData.loadAppData = function(fetch) { 
     var deferred = $q.defer(); 
     var getUser = $q.defer(); 
     var getPricing = $q.defer(); 
     var getBilling = $q.defer(); 

     if (!fetch.user || coreData.currentUser) { 
     getUser.resolve(); 
     } else { 
     $http 
      .get('https://example.com/api/getUser') 
      .success(function(result){ 
      coreData.currentUser = result; 
      getUser.resolve(); 
      }).error(function(reason) { 
      getUser.reject(reason); 
      }); 
     } 

     if (!fetch.billing || coreData.billing) { 
     getBilling.resolve(); 
     } else { 
     $http 
      .get('https://example.com/api/getBilling') 
      .success(function(result) { 
      coreData.billing = result; 
      getBilling.resolve(); 
      }).error(function(reason) { 
      getBilling.reject(reason); 
      }); 
     } 

     if (!fetch.pricing || coreData.pricing) { 
     getPricing.resolve(); 
     } else { 
     $http 
      .get('https://example.com/api/getPricing') 
      .success(function(result) { 
      coreData.pricing = result; 
      getPricing.resolve(); 
      }).error(function(reason) { 
      getPricing.reject(reason); 
      }); 
     } 

     $q.all([getPricing.promise, getUser.promise, getBilling.promise]).then(function(result) { 
     deferred.resolve(coreData); 
     }, function(reason){ 
     deferred.reject(reason); 
     }); 

     return deferred.promise; 
    }; 

    return appData; 
    }); 
+1

請注意'.success'和'.error'方法已被棄用,並已從[AngularJS 1.6中移除](https://github.com/angular/angular.js/pull/15157)。另外,由於$ http服務已經返回承諾,因此不需要使用'$ q.defer'來製造承諾。有關更多信息,請參閱[是否是「延遲反模式」?](http://stackoverflow.com/questions/30750207/is-this-a-deferred-antipattern) – georgeawg