Javascript Angular: 如何链接未知数量的 promises。

7

我在编写一个Angular.js服务,用于拉取一些JSON数据源。最初,服务不知道要请求哪些/多少资源; 它们依赖于另一个请求返回的ids。

我正在头疼如何链接$http服务请求。有没有常用的模式来做到这一点?

我尝试了另一个线程上建议的Array.reduce技术,但是在同步ids和请求的数据时遇到了问题。

这是我目前的代码。有人有什么建议吗?

aService.factory('dummy', function($http){
    // Dummy resources.
    var resources = [   
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js'
    ];
    return {
        data: function(callback){

            var jquerySources = []

            var promiseChain = $http({method: 'GET', url: resources[0]});
            var l = resources.length
            for (var i = 1; i < l; i++ ){
                promiseChain.then(function(data){

                    jquerySources.push({
                        url: resources[i],
                        source: data
                    });

                    promise_chain = $http({method: 'GET', url: resources[i]});
                    if (i === l){
                        return callback(jquerySources);
                    }
               });
            }
        }
    }
});

谢谢。


你是否真的需要逐个加载每个资源?如果不需要,你可以不断发出多个请求,然后等待所有承诺被解决吗? - codemonkey
你需要它们按顺序执行还是可以并行运行? - Benjamin Gruenbaum
如果你正在考虑 Promise 链式调用,你应该看看 async/await。它会让你的生活更轻松。 - Stephen Quan
2个回答

7
如果您需要按顺序执行请求,您可以创建一个 Promise 作为链的头部。然后,您可以将 $http 调用链接到该头部并解决头部 Promise:
aService.factory('seq', function($http, $q){
    // Dummy resources.
    var resources = [   
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js'
    ];
    var deferred = $q.defer();
    var resourcePromise = deferred.promise;
    var res = [];
    angular.forEach(resources, function(resource){
      return resourcePromise.then(function(){
        return $http({ method: 'GET', url: resource });
      }).then(function(data){
        res.push({res: resource, data : data});
      });
    });

    deferred.resolve();

    return {
        getResource: resourcePromise.then(function(){
          return res;
        })
    };
});

但如果请求是并行的,那么这将是更简单的解决方案。只需要一个承诺数组,然后调用$q.all函数等待所有承诺都被解决。

aService.factory('par', function($http, $q){
    // Dummy resources.
    var resources = [   
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js',
      'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js'
    ];
    var promises = [];
    var res = [];
    angular.forEach(resources, function(resource){
      promises.push(
        $http({ method: 'GET', url: resource }).then(
          function(data){
            res.push({res: resource, data : data});
          })
        );
    });

    return {
        getResource: $q.all(promises).then(function(){
          return res;
        })
    };
});

同样需要注意的是,在这两种情况下,我们都有一个名为“res”的数组来收集请求的结果。
编辑: 示例Plunker

我刚刚在寻找类似的东西,这太棒了Oleksii。这可能是一个有点低级的问题,但我随后尝试将数据传递到控制器,并遇到了麻烦。我可以在工厂中的$http函数中使用console.log输出数据,但无法在控制器的scope变量中获取json输出。您有关于如何最好执行此操作的任何建议吗? - Daniel F
基本上,在调用服务后,您需要使用.then()函数。我刚刚在我的答案中添加了一个带有示例的plunker,请查看它。 - Oleksii Aza
太棒了,Oleksii,非常感谢。我也投票支持了你的回答。感谢你的帮助。 - Daniel F
谢谢您的回答!如果在链接Promise之后还需要执行最后一步操作,您可以在其后加上一个额外的resourcePromise.then(()=>{finalWork();})。 - Lin Song Yang
我认为你的顺序示例实际上并不是顺序的。你需要重新分配承诺或使用reduce()。 - graywh

3

您确实可以使用reduce来完成此操作:

var chain = resources.reduce(function (sourcesPromise, url) {
    return sourcesPromise.then(function (sources) {
        return $http({method: 'GET', url: url})
        .then(function (data) {
            sources.push({url: url, source: data});

            return sources;
        });
    });
}, $q.when([]));

chain.then(function (sources) {
    // [{url, source}, ...]
});

基于如何在Q中链接可变数量的promise,以确保顺序?


这是一篇关于使用reduce进行Promise链式调用的优秀文章:https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/ - Evan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接