AngularJS承诺、$q、延迟对象

16

编辑

第一个答案很优雅,但是,正如在这个问题和另一个stackoverflow问题中多次说明的那样,问题在于服务和控制器在数据实际到达之前运行其操作。

(第一个答案的最后一条评论:)

是的,问题在于API调用完成后,服务才运行并将所有内容返回给控制器,请参见此处screencast.com/t/uRKMZ1IgGpb7 ... 这是我的基本问题,如何等待所有部分的数据到达?

就像我反复说的那样,我们如何创建一个在成功检索数据后填充数组的服务,并且在所有这些操作发生后控制器获取数据,因为正如您在我的截图中看到的那样,事情按不同的顺序进行。


我有这段代码:

 var deferred = $q.defer();
            $http.get('../wordpress/api/core/get_category_posts/?category_id=14 ').success(function(data) {
                //we're emptying the array on every call
                theData = [];
                catName = data.category.slug;
                theData = data;
                theData.name = catName;
                aggregatedData.push(theData);
            });
            $http.get('../wordpress/api/core/get_category_posts/?category_id=15 ').success(function(data) {
                theData = [];
                catName = data.category.slug;
                theData = data;
                theData.name = catName;
                aggregatedData.push(theData);
            });
            $http.get('../wordpress/api/core/get_category_posts/?category_id=16 ').success(function(data) {
                theData = [];
                catName = data.category.slug;
                theData = data;
                theData.name = catName;
                aggregatedData.push(theData);
            });
            $http.get('../wordpress/api/core/get_category_posts/?category_id=17 ').success(function(data) {
                theData = [];
                catName = data.category.slug;
                theData = data;
                theData.name = catName;
                aggregatedData.push(theData);
            });
            //deferred.resolve(aggregatedData);
            $timeout(function() {
                deferred.resolve(aggregatedData);
            }, 1000);
            /*//deferred.reject('There is a connection problem.');
            if (myservice._initialized) {
                $rootScope.$broadcast('postsList', deferred.promise);
            }*/
            //myservice._initialized = true;
            myservice = deferred.promise;
            return deferred.promise;

我真的不明白为什么在将结果数组传递给defer时要设置超时时间?

难道原则不应该是,defer等待信息返回并返回promise吗?那1秒钟有什么意义呢?据我所知,defer应该能够无限期等待API返回结果,然后返回承诺的数据。

我真的很困惑,在过去的两个小时里,我一直在为我的控制器没有接收到任何数据而烦恼,只有当我设置了超时时间后才得到数据。


你是不是想把所有 $http.get 的返回值放到 $q.all 中? - Steve Klösters
我将所有输出的数组都放入了aggregatedData中,以便将该对象传递给$q延迟。我想了解实际发生了什么,在这里有一个jsFiddle链接:http://jsfiddle.net/tjnWQ/ .... 这里的承诺是如何工作的。 - Arthur Kovacs
说实话,我感到非常沮丧。我已经花了将近6周的时间来掌握Angular,我真的很喜欢它的理念,我了解一些细节,我已经开发了3-4个小应用程序,但我的Angular框架知识中还存在一些巨大的漏洞,这妨碍了我制作一个真正可用的产品 :)。最后我不得不花很多钱购买了这本书:http://www.packtpub.com/angularjs-web-application-development/book。 - Arthur Kovacs
第一个答案是优雅的,"...问题是什么? - George
当时,我无法掌握和使用Angular中的Promise概念,并且在互联网上找到了一些误导性的文章,其中一些人实际上使用$timeout [某种方式]与多个Promise的返回连接,长话短说,我的头脑中充满了混乱的想法,而@dluz在下面的答案中让一切变得清晰明了。 - Arthur Kovacs
2个回答

60

在我看来,使用$q.all有一种更加聪明(且优雅)的方式来完成这个任务。

请看下面的代码:

我假设你想要一次性返回所有结果,将它们汇总到一个大数组中。

var myApp = angular.module('myApp', []);

myApp.factory('myService', function ($http, $q) {
    return {
        getAllData: function () {
            return $q.all([
                $http.get('../wordpress/api/core/get_category_posts/?category_id=14'),
                $http.get('../wordpress/api/core/get_category_posts/?category_id=15'),
                $http.get('../wordpress/api/core/get_category_posts/?category_id=16'),
                $http.get('../wordpress/api/core/get_category_posts/?category_id=17')
            ]).then(function (results) {
                var aggregatedData = [];
                angular.forEach(results, function (result) {
                    aggregatedData = aggregatedData.concat(result.data);
                });
                return aggregatedData;
            });
        }
    };
});

你可以看到上面的代码,aggregatedData只有在所有异步调用都通过$q.all完成后才会生成。

你只需要将该服务作为依赖项之一包含到其中一个控制器中,并像这样调用该服务:myService.getAllData()

如果你需要完整的工作示例,我可以提供一个!:)


14
支持使用 $q.all,同时为了更加DRY(Don't Repeat Yourself):[14, 15, 16, 17].map(function (id) { return $http.get('../wordpress/api/core/get_category_posts/?category_id=' + id); }); - plalx
是的!同意_plalx_的观点!只是不想用一些多余的甜言蜜语来使他感到困惑 :) - Denison Luz
太赞了!它看起来肯定比我的更优雅 :). 关于我上面的困扰:我应该假设$q.all会等待所有API调用完成,然后在最终数组中返回数据。这些数据也可以在控制器中使用.then()等待吗? - Arthur Kovacs
.then() 应该等待并将所有内容放入 aggregatedData 中,只有当所有数据都到位时才能这样做,但它正在失控 :) - Arthur Kovacs
1
Arthur,我有同样的一本书 - 实际上我很幸运地在伦敦认识了这本书的作者之一(Peter Bacon):) 我真的很喜欢这本书。看看第84页至94页(特别是第91页),你会更好地了解$q.all。乐意帮忙! - Denison Luz
显示剩余2条评论

11

$http.get调用是异步的,但在解决deferred之前您并没有等待它们全部完成。 这里使用超时可以工作,只是因为您很幸运,在1秒内调用已经完成,但这完全不可靠。

我不会在这里重申完整的解决方案,但看一下我的答案以获取另一个类似问题的解决方案。


我知道,那正是我所说的,超时(timeout)是在演示中广泛使用的一个属性,而不是用于真实的API通信。这就是我正在寻找的,一种等待并在两端(服务->控制器->视图)显示数据的方法。 - Arthur Kovacs
嘿plalx,能不能请你看一下第一个答案和我的评论,代码更干净了,但还存在时间问题http://screencast.com/t/uRKMZ1IgGpb7 - 数据仍然在到达之后才会显示。 - Arthur Kovacs
嘿Plalx,这就是关键,我之前尝试过(另一个问题在这里),使用来自数组的数据没有问题,但是使用我的机器上的真实端点时,我仍然遇到了那个问题。不过,我会用实际的代码片段制作一个fiddle。几分钟后。 - Arthur Kovacs
我在考虑尝试使用https://github.com/mgonto/restangular/blob/master/README.md。我也会尝试其他的东西。PLALX,我希望今晚能将我的应用程序推送到Heroku上线,并带回一个链接。 - Arthur Kovacs
@ArthurKovacs,很高兴它运行正常了;)!祝你余下的工作顺利! - plalx
显示剩余3条评论

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