Подтвердить что ты не робот

Angularjs $q.all

Я реализовал $q.all в angularjs, но я не могу заставить код работать. Вот мой код:

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 в моей службе.

4b9b3361

Ответ 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);
})
  • Когда вы пишете var deferred= $q.defer(); внутри цикла for, он поднимает в начало функции, это означает, что javascript объявляет эту переменную в области действия вне for loop.
  • В каждом цикле последний отложенный переопределяет предыдущий, для сохранения ссылки на этот объект не существует области уровня блока.
  • При вызове асинхронных обратных вызовов (успех/ошибка) они ссылаются только на последний отложенный объект, и только он разрешается, поэтому $q.all никогда не разрешается, потому что он все еще ждет других отложенных объектов.
  • Вам нужно создать анонимную функцию для каждого элемента, который вы выполняете.
  • Поскольку функции имеют области видимости, ссылка на отложенные объекты сохраняется в closure scope даже после выполнения функций.
  • Как прокомментировал #dfsq: нет необходимости вручную создавать новый отложенный объект, так как $http сам возвращает обещание.

Решение с 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);
}

Ответ 2

$http тоже обещание, вы можете сделать это проще:

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

Ответ 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);
        }