我如何使用来自 angular 工厂的循环发送程序请求(一个接一个)?

how do I send procedural requests (one after the other) with loop from angular factory?

我希望获得用户从 Spotify 关注的所有艺术家。问题是我一次只能请求 50 位艺术家,并且只有在第一次请求后才知道艺术家的总数。我的工厂是这样的:

app.factory('artists', [
    "$rootScope", 
    "$http",
    "$cookies",
    "$q",
    function($rootScope, $http, $cookies, $q){

    return {
        getUserFollowing: function(){
            var allArtists = [];
            var _params = {
                type: 'artist',
                limit: 50, // maximum
            };
            var _authConfig = {
                'Authorization': 'Bearer ' + $cookies.get('spotify-token'),
                'Content-Type': 'application/json'
            };

            var req = {
                method: 'GET',
                url: 'https://api.spotify.com/v1/me/following',
                headers: _authConfig,
                params: _params
            };
            return $http(req).then(function(res){

                var artistTotal = res.data.artists.total;
                allArtists = allArtists.concat(res.data.artists.items);

                if(allArtists.length < artistTotal){
                    // send the request again to load 50 more artists
                    return $http({
                        method: 'GET',
                        url: res.data.artists.next,
                        headers: _authConfig,
                    }).then(function(nextRes){
                        allArtists = allArtists.concat(nextRes.data.artists.items);
                        return allArtists;
                    });
                } else {
                    // need to return a promise
                    var dfd = $q.defer();
                    dfd.resolve(allArtists);
                    return dfd.promise;
                }
            });
        }
    };
}]);

在上面的请求中,如果用户关注超过 50 位艺术家,我会发送一个额外的请求(另外 50 个)。如果他们关注的艺术家少于 50 人,则总数将达到 return。我正在通过这样的控制器使用这个工厂:

// grab the artists they follow
artists.getUserFollowing().then(function(artistsUserFollows){
    $rootScope.artistTotal = artistsUserFollows.length;
    $rootScope.artists = artistsUserFollows;
});

我如何编写一个循环,以便 getUserFollowing() 将 return 一个包含所有艺术家的数组,无论他们是否关注任何艺术家或 1000 位艺术家?如果他们关注 1000 位艺术家,那么我们需要发送 20 个请求 (1000/50),或者如果他们没有关注任何艺术家,则只需发送一个请求。

如何发送动态数量的请求以获取用户关注的所有艺术家,一次 50 个?此外,请求的下一个 url(无身份验证 header)在 res.data.artists.next

中定义

更新的解决方案

我认为你需要一个递归的解决方案来解决这个问题。我没有测试过这个,但我认为它应该可以工作。如果您有任何问题,请告诉我进展情况。

var recursiveFunc = function(promises, allArtists, next) {

var req = {
    method: 'GET',
    url: next || 'https://api.spotify.com/v1/me/following',
    headers: _authConfig,
    params: _params
};

return $http(req)
    .then(function(res) {

        var artistTotal = res.data.artists.total;

        for(var i=0; i<res.data.artists.items; i++) {
            allArtists.push(res.data.artists.items[i]);
        }

        if (allArtists.length < artistTotal) {
            promises.push(recursiveFunc(res.data.artists.next));
        }
    });
}

var promises = [];
var allArtists = [];

recursiveFunc(promises, allArtists);

$q.all(promises).then(function() {
    console.log('All Artists', allArtists);
});

旧的解决方案

更新:这个答案不再相关,因为 OP 将问题从 "concurrent" 请求更改为 "procedural" 请求。


快速浏览了 Spotify 的 API 详细信息后,似乎 并发 无法解决您的问题。 Spotify 使用基于游标的分页,其中每个 HTTP 响应都包含用于检索下一组项目的游标。我相信这是他们针对可能的拒绝服务攻击选择的一种防御机制。

https://developer.spotify.com/web-api/object-model/#cursor-based-paging-object

我认为唯一的选择将与您已经在使用的非常相似,即将多个承诺链接在一起,直到您检索到所有艺术家。

编辑:将并行替换为并发以避免混淆。 :)

这是处理此问题的一种解决方案。使用单一承诺让您的应用程序知道所有艺术家的检索何时完成。该函数的每次迭代都会向下一个 url 发出请求(从最初的 spotify api 调用开始),附加它接收到的项目,然后在需要更多项目时调用自身,或者如果没有,则使用内置数组解决承诺。

这只是一个简单示例,您可能需要根据自己的用途进行修改。类似于以下内容:

function getAllArtists() {
  var deferred = $q.defer();
  var allArtists = [];

  var getArtists = function (url) {
    var req = { 
      // request details
      method: 'GET',
      url: url
      // ... other req options
    };

    $http(req).then(function (res) {
      var artistTotal = res.data.artists.total;
      allArtists = allArtists.concat(res.data.artists.items);

      if(allArtists.length < artistTotal) {
        // need more artists
        getArtists(res.data.artists.next);
      } else {
        // we have all artists
        deferred.resolve(allArtists);
      }
    }
  }

  getArtists('https://api.spotify.com/v1/me/following');

  return deferred.promise;
}