我的承诺返回代码有问题,我有一个函数getTagQuotes
,其中包含一个for循环,它可以使多个调用成为一个API,将数据返回到数组中。
我的代码如何从下面开始:
// If there are tags, then wait for promise here:
if (tags.length > 0) {
// Setting promise var to getTagQuotes:
var promise = getTagQuotes(tags).then(function() {
console.log('promise =',promise);
// This array should contain 1-3 tags:
console.log('tweetArrayObjsContainer =',tweetArrayObjsContainer);
// Loop through to push array objects into chartObj:
for (var i=0; i<tweetArrayObjsContainer.length; i++) {
chartObj.chartData.push(tweetArrayObjsContainer[i]);
}
// Finally draw the chart:
chartDirective = ScopeFactory.getScope('chart');
chartDirective.nvd3.drawChart(chartObj.chartData);
});
}
我的getTagQuotes函数带有promise:
function getTagQuotes(tags) {
var deferred = $q.defer(); // setting the defer
var url = 'app/api/social/twitter/volume/';
// My for loop, which only returns ONCE, even if there are 3 tags
for (var i=0; i<tags.length; i++) {
var loopStep = i;
rawTagData = [];
// The return statement
return GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
.success(function(data, status, headers, config) {
rawTagData.push(data);
// One the last loop, call formatTagData
// which fills the tweetArrayObjsContainer Array
if (loopStep === (rawTagData.length - 1)) {
formatTagData(rawTagData);
deferred.resolve();
return deferred.promise;
}
});
}
function formatTagData(rawData) {
for (var i=0; i<rawData.length; i++) {
var data_array = [];
var loopNum = i;
for (var j=0; j<rawData[loopNum].frequency_counts.length; j++) {
var data_obj = {};
data_obj.x = rawData[loopNum].frequency_counts[j].start_epoch;
data_obj.y = rawData[loopNum].frequency_counts[j].tweets;
data_array.push(data_obj);
}
var tweetArrayObj = {
"key" : "Quantity"+(loopNum+1), "type" : "area", "yAxis" : 1, "values" : data_array
};
tweetArrayObjsContainer.push(tweetArrayObj);
}
}
}
注意这一行
return GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
它在我的for循环中:
for (var i=0; i<tags.length; i++)
如果我只需要循环一次,那么一切都很有效。但是,只要有另一个标签(最多3个),它仍然只返回第一个循环/数据。它不会等到for循环完成。然后回复承诺。所以我的tweetArrayObjsContainer
始终只有第一个标记。
答案 0 :(得分:2)
三个问题:
getTagQuotes
方法返回延期承诺。i
以查看您是否通过循环,并且for循环已经完成(i == (tags.length - 1)
),然后才能调用第一个成功。return
,这样你甚至没有进入第二项。此处已更正的代码(尚未对其进行测试)
function getTagQuotes(tags) {
var deferred = $q.defer(); // setting the defer
var url = 'app/api/social/twitter/volume/';
var tagsComplete = 0;
for (var i=0; i<tags.length; i++) {
rawTagData = [];
GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
.success(function(data, status, headers, config) {
rawTagData.push(data);
tagsComplete++;
if (tagsComplete === tags.length) {
formatTagData(rawTagData);
deferred.resolve();
}
});
}
return deferred.promise;
}
答案 1 :(得分:2)
return deferred.promise;
应该是您的函数的返回值,而不是GetTweetVolFactory.returnTweetVol()
,因为这是您打算宣传的内容。
您的问题是您正在呼叫多个GetTweetVolFactory.returnTweetVol()
,然后您需要合并所有这些异步调用以解决您的承诺。为了做到这一点,你应该只宣传一个GetTweetVolFactory.returnTweetVol()
电话:
function promisifiedTweetVol(rawTagData, urlStuff) {
var deferred = $q.defer(); // setting the defer
GetTweetVolFactory.returnTweetVol(urlStuff)
.success(function(data, status, headers, config) {
rawTagData.push(data);
// One the last loop, call formatTagData
// which fills the tweetArrayObjsContainer Array
if (loopStep === (rawTagData.length - 1)) {
formatTagData(rawTagData);
deferred.resolve();
}
});
return deferred.promise;
}
然后在循环中调用每个promise并返回在所有promise完成时解析的promise:
function getTagQuotes(tags) {
var url = 'app/api/social/twitter/volume/';
var promises = [];
// My for loop, which only returns ONCE, even if there are 3 tags
for (var i=0; i<tags.length; i++) {
var loopStep = if;
rawTagData = [];
promises.push( promisifiedTweetVol(rawTagData, url+tags[i].term_id) );
}
// ...
return $.when(promises);
}
您的代码还有一些问题,但您应该能够使用我的提示。
答案 2 :(得分:1)
你应该在这里返回一个promises数组,这意味着你应该改变这样的getTagsQuotes:
function getTagQuotes(tags) {
var url = 'app/api/social/twitter/volume/',
promises = [];
for (var i=0; i<tags.length; i++) {
promises.push( GetTweetVolFactory.returnTweetVol( url+tags[i].term_id ) );
}
return promises;
}
然后循环这样的承诺:
if (tags.length > 0) {
var promises = getTagQuotes(tags);
promises.map( function( promise ) {
promise.then( function( data ) {
//Manipulate data here
});
});
}
修改:如果您希望按照评论中的说明完成所有承诺,则应执行以下操作:
if (tags.length > 0) {
Promise.all( getTagQuotes(tags) ).then( function( data ) {
//Manipulate data here
});
}
修改:完整数据操作:
Promise.all( getTagQuotes(tags) ).then( function( allData ) {
allData.map( function( data, dataIndex ){
var rawData = data.data,
dataLength = rawData.frequency_counts.length,
j = 0,
tweetArrayObj = {
// "key" : "Quantity"+(i+1),
// "color" : tagColorArray[i],
"key" : "Quantity",
"type" : "area",
"yAxis" : 1,
"values" : []
};
for ( j; j < dataLength; j++ ) {
rawData.frequency_counts[j].start_epoch = addZeroes( rawData.frequency_counts[j].start_epoch );
tweetArrayObj.values.push( { x:rawData.frequency_counts[j].start_epoch, y:rawData.frequency_counts[j].tweets } );
}
tweetArrayObjsContainer.push( tweetArrayObj );
});
for ( var i= 0,length = tweetArrayObjsContainer.length; i < length; i++ ) {
chartObj.chartData.push( tweetArrayObjsContainer[ i ] );
}
chartDirective = ScopeFactory.getScope('chart');
chartDirective.nvd3.drawChart(chartObj.chartData);
});
答案 3 :(得分:1)
将每个承诺放在一个数组中然后执行:
$q.all(arrayOfPromises).then(function(){
// this runs when every promise is resolved.
});
答案 4 :(得分:0)
使用延迟被广泛认为是一种反模式。如果您的promise库支持promise contstructor,那么这就是创建自己的承诺的更简单方法。
我通常使用具有all
函数的promise实现,而不是尝试在一个中解析所有promise。然后我创建一个函数,它返回一个事物的promise,然后是另一个函数,它返回所有事物的promise。
使用map()
函数通常比使用for
循环更清晰。
这是一个通用食谱。假设你的promise实现有一些all
函数的味道:
var fetchOne = function(oneData){
//Use a library that returns a promise
return ajax.get("http://someurl.com/" + oneData);
};
var fetchAll = function(allData){
//map the data onto the promise-returning function to get an
//array of promises. You could also use `_.map` if you're a
//lodash or underscore user.
var allPromises = myData.map(fetchOne);
return Promise.all(allPromises);
};
var allData = ["a", "b", "c"];
var promiseForAll = fetchAll(allData);
//Handle the results for all of the promises.
promiseForAll.then(function(results){
console.log("All done.", results);
});
答案 5 :(得分:0)
参考这个问题和earlier question:
for
循环,代码通常会更清晰。getTagQuotes()
将通过构建一系列承诺,将其提交到$q.all()
并返回总承诺来变得更清晰。formatTagData()
及其与其来电者的关系将通过返回已转换的rawData
来变得更干净。通过一些假设,代码应该简化为:
getTagQuotes(tags).then(function(tweetArrayObjsContainer) {
chartObj.chartData = chartObj.chartData.concat(tweetArrayObjsContainer); // concat() ...
// chartObj.chartData = tweetArrayObjsContainer; // ... or simply assign??
chartDirective = ScopeFactory.getScope('chart');
chartDirective.nvd3.drawChart(chartObj.chartData);
});
function getTagQuotes(tags) {
var url = 'app/api/social/twitter/volume/';
var promises = tags.map(function(tag) {
var deferred = $q.defer();
GetTweetVolFactory.returnTweetVol(url + tag.term_id)
.success(function(data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status) {
console.log(tag.term_id + ': error in returning tweet data');
deferred.resolve(null); // resolve() here effectively catches the error
});
return deferred.promise;
});
return $q.all(promises).then(formatTagData); //this is much much cleaner than building an explicit data array and resolving an outer deferred.
function formatTagData(rawData) {
return rawData.filter(function(data) {
return data || false; // filter out any nulls
}).map(function(item, i) {
return {
'key': 'Quantity' + (i+1),
'type': 'area',
'yAxis': 1,
'color': tagColorArray[i],
'values': item.frequency_counts.reverse().map(function(c) {
return {
x: addZeroes(c.start_epoch),
y: c.tweets,
};
})
};
});
}
}