【问题标题】:Angularjs $q.allAngularjs $q.all
【发布时间】:2014-02-14 03:58:44
【问题描述】:

我已经在 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 存在一些问题。

【问题讨论】:

  • 您看到了什么行为?它会调用你的then(datas) 吗?试试push这个:promises.push(deffered);
  • @themyth92 你试过我的解决方案了吗?
  • 我已经尝试过,两种方法都适用于我的情况。但我会让@Llan Frumer 作为正确答案。真的谢谢你们俩。
  • 你为什么要承诺化现有的承诺? $http 已经返回了一个承诺。使用 $q.defer 是多余的。
  • deferred 不是deffered :)

标签: angularjs promise q


【解决方案1】:

在javascript中没有block-level scopes只有function-level scopes

阅读这篇关于javaScript Scoping and Hoisting的文章。

看看我是如何调试你的代码的:

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);
})
  • 当您在 for 循环中编写 var deferred= $q.defer(); 时,它会提升到函数的顶部,这意味着 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);
}

【讨论】:

  • 好答案。一个补充:不需要构造 new deferred 因为 $http 本身返回承诺。所以它可以更短:plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
  • “当你在 for 循环中编写 var deferred= $q.defer(); 时,它会被提升到函数的顶部。”。这部分我没看懂,能解释一下背后的原因吗?
  • 我知道,实际上我也会这样做。
  • 喜欢使用map 来构建一系列承诺。非常简单明了。
  • 需要注意的是声明被提升了,但是赋值保持在原处。此外,现在还有带有“let”语句的块级范围。见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案2】:

$http 也是一个承诺,你可以让它更简单:

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

【讨论】:

  • 是的,公认的答案只是反模式的集合。
  • 我认为您可以将.then() 子句排除在外,因为 OP 想在他的控制器中执行所有这些操作,但原则是完全正确的。
  • 当然你可以省略 then 子句,我添加了它以防你想要记录所有 HTTP 请求,我总是添加它们并使用全局 onError 回调来处理所有服务器异常。跨度>
  • @Zerkotin 你可以从.thenthrow 以便以后处理它将它暴露给$exceptionHandler,这样可以省去你的麻烦和全球。
  • 不错。这与接受的答案的最后一个解决方案/示例基本相同。
【解决方案3】:

问题似乎是您添加了deffered.promise,而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);
        }

【讨论】:

  • 这只返回一个延迟对象数组,我检查了。
  • 我不知道他说什么,只知道控制台说什么,你可以看到它不起作用:plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
  • 文档也清楚地表明$q.all 得到了承诺而不是延迟对象。 OP 的真正问题在于范围界定,因为只有最后一个 deferred 才能得到解决
  • Ilan,感谢您解开defer 对象和promises。你也解决了我的all() 问题。
  • 问题已在 2 个答案中得到解决,问题是作用域或变量提升,不管你想怎么称呼它。
猜你喜欢
  • 1970-01-01
  • 2014-04-05
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 2014-01-01
  • 2018-03-13
  • 2014-10-06
  • 1970-01-01
相关资源
最近更新 更多