2016-12-05 33 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