我在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:我是法国人,所以请放纵我的英语!答案 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;
}