AngularJS $ q.all


106

我已经在angularjs中实现了$ q.all,但是我无法使代码正常工作。这是我的代码:

UploadService.uploadQuestion = function(questions){

        var promises = [];

        for(var i = 0 ; i < questions.length ; i++){

            var deffered  = $q.defer();
            var question  = questions[i]; 

            $http({

                url   : 'upload/question',
                method: 'POST',
                data  : question
            }).
            success(function(data){
                deffered.resolve(data);
            }).
            error(function(error){
                deffered.reject();
            });

            promises.push(deffered.promise);
        }

        return $q.all(promises);
    }

这是我的控制器,它调用服务:

uploadService.uploadQuestion(questions).then(function(datas){

   //the datas can not be retrieved although the server has responded    
}, 
function(errors){ 
   //errors can not be retrieved also

})

我认为在我的服务中设置$ q.all存在一些问题。


1
您看到什么行为?它会打给您then(datas)吗?试试push这个:promises.push(deffered);
Davin Tryon 2014年

@ themyth92您是否尝试过我的解决方案?
Ilan Frumer 2014年

我已经尝试过,两种方法都适用于我的情况。但我会将@Llan Frumer作为正确答案。真的谢谢你们
themyth92

1
您为什么要承诺兑现现有的承诺?$ http已经返回一个承诺。$ q.defer的使用是多余的。
皮特·阿尔文

1
这是deferred不是deffered:)
克里斯托夫•鲁西

Answers:


225

在javascript中block-level scopes不仅有function-level scopes

阅读有关javaScript作用域和提升的文章。

看看我如何调试您的代码:

var deferred = $q.defer();
deferred.count = i;

console.log(deferred.count); // 0,1,2,3,4,5 --< all deferred objects

// some code

.success(function(data){
   console.log(deferred.count); // 5,5,5,5,5,5 --< only the last deferred object
   deferred.resolve(data);
})
  • 当您var deferred= $q.defer();在for循环内编写代码时,它会被提升到函数顶部,这意味着javascript在之外的函数范围内声明了此变量for loop
  • 对于每个循环,最后一个延迟的优先级将覆盖前一个循环,没有块级作用域来保存对该对象的引用。
  • 调用异步回调(成功/错误)时,它们仅引用最后一个延迟的对象,并且仅对其进行解析,因此$ q.all永远不会解析,因为它仍在等待其他延迟的对象。
  • 您需要为要迭代的每个项目创建一个匿名函数。
  • 由于函数确实具有作用域,因此closure scope即使在函数执行后,对延迟对象的引用也会保留下来。
  • 正如#dfsq所评论的:由于$ http本身会返回一个promise,因此无需手动构造新的延迟对象。

解决方案angular.forEach

这是一个演示插件:http ://plnkr.co/edit/NGMp4ycmaCqVOmgohN53?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = [];

    angular.forEach(questions , function(question) {

        var promise = $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

        promises.push(promise);

    });

    return $q.all(promises);
}

我最喜欢的方法是使用Array#map

这是一个演示插件:http ://plnkr.co/edit/KYeTWUyxJR4mlU77svw9?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = questions.map(function(question) {

        return $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

    });

    return $q.all(promises);
}

14
好答案。补充:由于$ http本身会返回promise,因此无需构造新的deferred。因此它可以更短:plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
dfsq 2014年

“当您编写var deferred = $ q.defer();时,它会在一个for循环中被提升到函数的顶部。” 我不明白这部分,您能解释其背后的原因吗?
themyth92

我知道,实际上我会这样做。
dfsq,2014年

4
喜欢使用map来建立一系列承诺。非常简单明了。
Drumbeg 2015年

1
应该注意的是,该声明是悬挂的,但是该分配保留在原处。而且,现在有了“ let”语句的块级作用域。见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
斯宾塞

36

$ http也是一个承诺,您可以使其更简单:

return $q.all(tasks.map(function(d){
        return $http.post('upload/tasks',d).then(someProcessCallback, onErrorCallback);
    }));

2
是的,公认的答案只是反模式的集合。
Roamer-1888

我认为您可以.then()省略该子句,因为OP希望在他的控制器中执行所有操作,但是原理是完全正确的。
Roamer-1888

2
当然,您可以省略then子句,如果您想记录所有HTTP请求,我会添加它,我总是添加它们并使用全局onError回调来处理所有服务器异常。
2014年

1
您可以throw从@Zerkotin .then中进行处理,以便以后处理它并将其暴露给$exceptionHandler,这可以为您省去麻烦和麻烦。
本杰明·格伦鲍姆

真好 这基本上与接受的答案的最后解决方案/示例相同。
Niko Bellic

12

问题似乎是您要添加deffered.promisewhen deffered本身就是您应该添加的承诺:

尝试更改为,promises.push(deffered);以免将未包装的承诺添加到数组中。

 UploadService.uploadQuestion = function(questions){

            var promises = [];

            for(var i = 0 ; i < questions.length ; i++){

                var deffered  = $q.defer();
                var question  = questions[i]; 

                $http({

                    url   : 'upload/question',
                    method: 'POST',
                    data  : question
                }).
                success(function(data){
                    deffered.resolve(data);
                }).
                error(function(error){
                    deffered.reject();
                });

                promises.push(deffered);
            }

            return $q.all(promises);
        }

我只检查了这只返回了一个有延迟对象的数组。
Ilan Frumer 2014年

我不知道他说了什么,只有控制台说了什么,您可以看到它不起作用:plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
Ilan Frumer 2014年

4
此外,文档还清楚地表明$q.all,承诺不会延迟对象。OP的真正问题在于范围界定,因为只有最后一个延期解决了
Ilan Frumer 2014年

宜兰,感谢您解开defer对象和promises。您也解决了我的all()问题。
罗斯·罗杰斯

这个问题在2个答案中得到了解决,无论您要叫什么问题,问题都是范围界定或变量提升。
Zerkotin 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.