javascript - Cum să returnezi o singură promisiune după ce bucla for (care produce o promisiune la fiecare iterație) s-a încheiat?
Am o problemă cu codul de returnare al promisiunii mele, am o funcție getTagQuotes care conține un ciclu for care poate face mai multe apeluri către o API pentru a returna date într-un tablou.
Cum începe codul meu pentru asta mai jos:
// 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);
});
}
Funcția mea getTagQuotes cu returnarea promise-ului:
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);
}
}
}
Acordă atenție acestei linii
return GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
este în interiorul buclei mele for:
for (var i=0; i<tags.length; i++)
Totul funcționează grozav dacă trebuie să parcurg doar o dată. Totuși, de îndată ce există un alt tag (până la 3) el returnează în continuare doar primul loop/date. Nu așteaptă până când for loop-ul se termină. Apoi returnează promisiunea. Deci tweetArrayObjsContainer conține întotdeauna doar primul tag.
Răspuns #1
Trei probleme:
- nu ai returnat promisiunea amânată din metoda
getTagQuotes. - te uitai la
ipentru a vedea dacă ai terminat bucla, și bucla for este deja completă (i == (tags.length - 1)) înainte ca primul succes să fie apelat. - ai apelat
returnîn prima iterație a buclei, astfel încât nici măcar nu ai ajuns la al doilea element.
Iată codul corectat (nu l-am testat încă)
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;
}
Răspunsul #2
return deferred.promise; ar trebui să fie valoarea returnată a funcției tale, nu GetTweetVolFactory.returnTweetVol(), pentru că asta intenționezi să transformi într-o promisiune.
Problema ta este că apelezi mai multe GetTweetVolFactory.returnTweetVol(), și apoi trebuie să combini toate acele apeluri asincrone pentru a rezolva promisiunea ta. Pentru a face asta, ar trebui să transformi în promisiune doar un singur apel 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;
}
Și apoi apelează fiecare promisiune într-un ciclu și returnează promisiunea care se rezolvă când toate promisiunile sunt finalizate:
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);
}
Mai există câteva probleme cu codul tău, dar ar trebui să reușești să îl faci să funcționeze cu sfatul meu.
