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.