Angularjs $ q.all


105

Tôi đã triển khai $ q.all trong anglejs, nhưng tôi không thể làm cho mã hoạt động. Đây là mã của tôi:

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);
    }

Và đây là bộ điều khiển của tôi gọi các dịch vụ:

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

})

Tôi nghĩ rằng có một số vấn đề khi thiết lập $ q.all trong dịch vụ của mình.


1
Bạn đang nhìn thấy hành vi nào? Nó có gọi vào của bạn then(datas)không? Cố gắng chỉ pushnày:promises.push(deffered);
Davin Tryon

@ themyth92 bạn đã thử giải pháp của tôi chưa?
Ilan Frumer

Tôi đã thử và cả hai phương pháp đều hoạt động trên trường hợp của tôi. Nhưng tôi sẽ coi @Llan Frumer là câu trả lời chính xác. Thực sự cảm ơn cả hai bạn.
themyth92,

1
Tại sao bạn thất hứa-izing một lời hứa hiện có? $ http đã trả lại một lời hứa. Sử dụng $ q.defer là không cần thiết.
Pete Alvin

1
deferredkhông phải là deffered:)
Christophe Roussy

Câu trả lời:


225

Trong javascript không có block-level scopeschỉ function-level scopes:

Đọc bài viết này về javaScript Scoping và Hoisting .

Xem cách tôi gỡ lỗi mã của bạn:

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);
})
  • Khi bạn viết var deferred= $q.defer();bên trong vòng lặp for, nó được đưa lên đầu hàm, điều đó có nghĩa là javascript khai báo biến này trên phạm vi hàm bên ngoài for loop.
  • Với mỗi vòng lặp, cuối cùng chậm được trọng trước đó, không có phạm vi khối cấp để lưu một tham chiếu đến đối tượng đó.
  • Khi gọi lại không đồng bộ (thành công / lỗi) được gọi, chúng chỉ tham chiếu đến đối tượng hoãn lại cuối cùng và chỉ nó được giải quyết, vì vậy $ q.all không bao giờ được giải quyết vì nó vẫn đang đợi các đối tượng hoãn khác.
  • Những gì bạn cần là tạo một chức năng ẩn danh cho mỗi mục bạn lặp lại.
  • Vì các hàm có phạm vi, nên tham chiếu đến các đối tượng được hoãn lại được bảo toàn closure scopengay cả sau khi các hàm được thực thi.
  • Như #dfsq đã nhận xét: Không cần phải tạo một đối tượng trì hoãn mới theo cách thủ công vì bản thân $ http trả về một lời hứa.

Giải pháp với angular.forEach:

Đây là một plunker demo: 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);
}

Cách yêu thích của tôi là sử dụng Array#map:

Đây là bản demo plunker: 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
Câu trả lời tốt. Một bổ sung: không cần phải tạo trì hoãn mới vì bản thân $ http trả về lời hứa. Vì vậy, nó có thể ngắn hơn: plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
dfsq

"Khi bạn viết var deferred = $ q.defer (); bên trong vòng lặp for, nó được đưa lên đầu hàm.". Tôi không hiểu phần này, bạn có thể giải thích lý do đằng sau nó?
themyth92,

Tôi biết, thực ra tôi cũng sẽ làm như vậy.
dfsq

4
Yêu thích việc sử dụng mapđể xây dựng một loạt các lời hứa. Rất đơn giản và ngắn gọn.
Drumbeg

1
Cần lưu ý rằng khai báo được nâng lên, nhưng nhiệm vụ vẫn ở nguyên vị trí của nó. Ngoài ra, hiện có phạm vi cấp khối với câu lệnh 'let'. Xem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Spencer,

36

$ http cũng là một lời hứa, bạn có thể làm cho nó đơn giản hơn:

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

2
Vâng, câu trả lời được chấp nhận chỉ là sự kết tụ của những phản mẫu.
Roamer-1888

Tôi nghĩ rằng bạn có thể bỏ .then()điều khoản vì OP muốn làm tất cả những điều đó trong bộ điều khiển của mình, nhưng nguyên tắc là hoàn toàn chính xác.
Roamer-1888

2
tất nhiên bạn có thể bỏ qua mệnh đề then, tôi đã thêm nó trong trường hợp bạn muốn ghi lại tất cả các yêu cầu HTTP, tôi luôn thêm chúng và sử dụng lệnh gọi lại onError toàn cục, để xử lý tất cả các trường hợp ngoại lệ của máy chủ.
Zerkotin

1
@Zerkotin bạn có thể throwtừ a .thenđể vừa xử lý nó sau này vừa để lộ nó ra $exceptionHandler, điều này sẽ giúp bạn tránh được rắc rối đó và toàn cầu.
Benjamin Gruenbaum

Đẹp. Về cơ bản, đây là cách tiếp cận tương tự như giải pháp / ví dụ cuối cùng của câu trả lời được chấp nhận.
Niko Bellic

12

Vấn đề dường như là bạn đang thêm thời deffered.promiseđiểm defferedchính là lời hứa mà bạn nên thêm:

Hãy thử thay đổi thành promises.push(deffered);để bạn không thêm lời hứa chưa được bao bọc vào mảng.

 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);
        }

Điều này chỉ trả về một mảng các đối tượng deffered, tôi đã kiểm tra nó.
Ilan Frumer

Tôi không biết những gì ông nói, chỉ có những gì giao diện điều khiển nói: Bạn có thể thấy nó không làm việc: plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
Ilan Frumer

4
Ngoài ra, tài liệu nói rõ ràng rằng $q.allnhận được lời hứa không phải đối tượng trì hoãn. Vấn đề thực sự của OP là với Phạm vi và bởi vì đó chỉ là cuối cùng hoãn được việc giải quyết
Ilan Frumer

Ilan, cảm ơn vì đã tách defercác đối tượng và promises. Bạn cũng đã khắc phục sự cố của tôi all().
Ross Rogers

vấn đề đã được giải quyết trong 2 câu trả lời, vấn đề là phạm vi hoặc cẩu biến, bạn muốn gọi nó là gì.
Zerkotin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.