地图循环中的角度承诺

时间:2015-08-23 10:23:40

标签: javascript angularjs dictionary promise

我在map循环中调用了一些承诺时遇到了一个奇怪的问题。

我将角度服务编码为具有远程API的接口。 API提供了一些获取调查数据的方法。

angular.module('viewerServices').factory('SurveyService', [

    '$q',
    '$http',
    'Debugger',
    'CONFIG',

    function ($q, $http, Debugger, CONFIG) {

        var data;
        /* survey data object structure
        {
            name: null,
            questions: [
                {
                    type: null,
                    text: null,
                    answers: [
                        {
                            text: null
                        }
                    ]
                }
            ]
        }
        */

        var getQuestionAnswers = function (question) {
            var deferred = $q.defer();
            var parse = function (str) {
                var answers = [];
                var answer;
                // answers list is defined like this: reponsetext1;weight1;responsetext2;weight2;...
                var fragments = str.split(';');
                fragments.forEach(function (fragment, index) {
                    if (index % 2 === 0) {
                        answer = {};
                        answer.text = fragment;
                    }
                    else if (index % 2 !== 0) {
                        answer.weight = fragment;
                        answers.push(answer);
                    }
                });
                return answers;
            };
            if (CONFIG.ENV !== 'local') {
                com.veeva.clm.getDataForObject('Survey_Question_vod__c', question.id, 'Answer_Choice_vod__c', function (response) {
                    question.answers = parse(response.Survey_Question_vod__c.Answer_Choice_vod__c);
                    deferred.resolve(question);
                });
            }
            else {
                $http.get('survey/survey.json').then(function (response) {
                    question.answers = parse(response.data.Survey_Question_vod__c[question.id].Answer_Choice_vod__c);
                    deferred.resolve(question);
                });
            }
            return deferred.promise;
        };

        var getQuestionText = function (question) {
            var deferred = $q.defer();
            if (CONFIG.ENV !== 'local') {
                com.veeva.clm.getDataForObject('Survey_Question_vod__c', question.id, 'Text_vod__c', function (response) {
                    question.text = response.Survey_Question_vod__c.Text_vod__c;
                    deferred.resolve(question);
                });
            }
            else {
                $http.get('survey/survey.json').then(function (response) {
                    question.text = response.data.Survey_Question_vod__c[question.id].Text_vod__c;
                    deferred.resolve(question);
                });
            }
            return deferred.promise;
        };

        var getSurvey = function () {
            var deferred = $q.defer();
            if (CONFIG.ENV !== 'local') {
                com.veeva.clm.getDataForCurrentObject("Presentation", "Survey_vod__c", function (response) {
                    deferred.resolve({
                        id: response.Presentation.Survey_vod__c
                    });
                });
            }
            else {
                $http.get('survey/survey.json').then(function (response) {
                    deferred.resolve({
                        id: response.data.Presentation.Survey_vod__c
                    });
                });
            }
            return deferred.promise;
        };

        var getSurveyName = function (survey) {
            var deferred = $q.defer();
            if (CONFIG.ENV !== 'local') {
                com.veeva.clm.getDataForObject("Survey_vod__c", survey.id, "Name", function (response) {
                    survey.name = response.Survey_vod__c.Name;
                    deferred.resolve(survey);
                });
            }
            else {
                $http.get('survey/survey.json').then(function (response) {
                    survey.name = response.data.Survey_vod__c[survey.id].Name;
                    deferred.resolve(survey);
                });
            }
            return deferred.promise;
        };

        var getSurveyQuestions = function (survey) {
            var deferred = $q.defer();
            survey.questions = [];
            if (CONFIG.ENV !== 'local') {
                com.veeva.clm.getSurveyQuestions_Survey(survey.id, function (response) {
                    response.Survey_Question_vod__c.forEach(function (question) {
                        survey.questions.push({
                            id: question.ID
                        });
                    });
                    deferred.resolve(survey);

                });
            }
            else {
                $http.get('survey/survey.json').then(function (response) {
                    response.data.SurveyQuestions_Survey[survey.id].forEach(function (question) {
                        survey.questions.push({
                            id: question.ID
                        });
                    });
                    deferred.resolve(survey);
                });
            }
            return deferred.promise;
        };


        var that = {

            getData: function () {
                var deferred = $q.defer();
                if (!data) {
                    data = {};
                    getSurvey().then(function (survey) {
                        // get survey's name
                        return getSurveyName(survey);
                    }).then(function (survey) {
                        // get survey's questions
                        return getSurveyQuestions(survey);
                    }).then(function (survey) {
                        // loop through survey's questions
                        var promises = survey.questions.map(function (question) {
                            // get question text
                            return getQuestionText(question).then(
                                function (q) {
                                    // get question's possible answers
                                    return getQuestionAnswers(q).then(
                                        function (q) {
                                            Debugger.log(q);
                                        },
                                        function (error) {
                                            Debugger.log(error);
                                        }
                                    );
                                },
                                function (error) {
                                    Debugger.log(error);
                                }
                            );
                        });
                        $q.all(promises).then(
                            function () {
                                data = survey;
                                deferred.resolve(data);
                            },
                            function (error) {
                                Debugger.log(error);
                            }
                        );
                    });
                }
                return deferred.promise;
            }

        };

        return that;
    }

]);

在服务中检索数据时,指令会显示带有拖放界面的调查。

angular.module('viewerDirectives').directive('slide', [

    '$timeout',
    'FlowService',
    'SurveyService',
    'Debugger',

    function ($timeout, FlowService, SurveyService, Debugger) {
        return {
            restrict: 'A',
            link: function ($scope, $element) {

                // PRIVATE PROPERTIES
                var answers = {};
                var $draggables;
                var $dropzones;


                // SCOPE PROPERTIES
                $scope.isValid = false;


                SurveyService.getData().then(function (data) {
                    $scope.questions = data.questions;

                    $timeout(function () {
                        $draggables = $element.find('.drag');
                        $dropzones = $element.find('.dropzone');
                        $dropzones.each(function () {
                            this.x =  $(this).offset().left;
                            this.y =  $(this).offset().top;
                        });

                        $draggables.each(function () {
                            $(this).on('mousedown mouseup', function (evt) {
                                evt.stopPropagation();
                            });
                            this.initX = $(this).offset().left;
                            this.initY = $(this).offset().top;
                        });

                        Draggable.create($draggables, {
                            type: 'x,y',
                            bounds: '.slide',
                            onPress: function () {
                                this.startX = this.x;
                                this.startY = this.y;
                            },
                            onDragStart: function () {
                                $(this.target).addClass('is-moving');
                            },
                            onDragEnd: function () {
                                var draggable = this;
                                var $parent = $(draggable.target).parent();
                                $(draggable.target).removeClass('is-moving');
                                draggable.dropped = false;
                                $dropzones.each(function (dropzoneIndex, dropzone) {
                                    if (draggable.hitTest(this, '50%') && !isOccupiedDropzone(dropzoneIndex)) {
                                        draggable.dropped = true;
                                        answers[draggable.target.textContent] = dropzoneIndex;
                                        TweenLite.to(draggable.target, 0.2, { ease: Expo.easeOut, x: this.x - draggable.target.initX, y: this.y - draggable.target.initY });
                                        //
                                        $scope.isValid = isValid();
                                        $scope.$apply();
                                        return false;
                                    }
                                });
                                if (draggable.hitTest($parent, '50%')) {
                                    answers[draggable.target.textContent] = null;
                                    TweenLite.to(draggable.target, 0.2, { ease: Expo.easeOut, x: 0, y: 0 });
                                    $scope.isValid = isValid();
                                    $scope.$apply();
                                    return;
                                }
                                if (!draggable.dropped) {
                                    TweenLite.to(draggable.target, 0.5, { ease: Expo.easeOut, x: draggable.startX, y: draggable.startY });
                                }
                            }
                        });
                    }, 0);
                });


                // PRIVATE METHODS
                var isOccupiedDropzone = function (index) {
                    for (var draggable in answers) {
                        if (answers[draggable] === index) {
                            return true;
                        }
                    }
                    return false;
                };

                var isValid = function () {
                    var valid = Object.keys(answers).length === $dropzones.length;
                    for (var draggable in answers) {
                        valid = valid && (answers[draggable] !== null);
                    }
                    return valid;
                };


                // SCOPE METHODS
                $scope.submit = function () {
                    // TODO
                };

            }
        };
    }
]);

A' local' mode允许我在本地服务器上模拟API调用。在这种模式下,一切正常,全局$q.all()承诺确实解析了预期的数据,并且指令显示了与预期一样多的可拖动元素。

但是当我打开' prod'模式,全局$q.all() promise永远不会被解析,因为只解析了map循环中的最后一个promises链。这就像我在循环中遇到了异步进程的基本问题,但map循环不能阻止这种情况吗?无论如何,我还测试了一个简单的for循环和一个闭包,结果相同。

欢迎任何帮助,因为我要发疯了!

PS:我是法国人,所以请放纵我的英语!

1 个答案:

答案 0 :(得分:0)

{1}}中缺少return

getData()

更进一步,您可能还会考虑缓存数据承诺而非return $q.all(promises).then(...); ,并进行大量整理,如下所示:

data

编辑:

尝试编写所有这样的getter函数:

var dataPromise;
...
getData: function () {
    if (!dataPromise) {
        function processQuestion(question) {
            return getQuestionText(question)
            .then(getQuestionAnswers)
            .then(Debugger.log, Debugger.log);
        }
        dataPromise = getSurvey()
        .then(getSurveyName)
        .then(getSurveyQuestions)
        .then(function (survey) {
            return $q.all(survey.questions.map(processQuestion)).then(function () {
                return survey;
            }, Debugger.log );
        });
    }
    return dataPromise;
}