javascript - Bagaimana untuk memulangkan satu janji tunggal selepas gelung for (yang menghasilkan janji pada setiap iterasi) selesai?
Saya mempunyai masalah dengan kod pulangan janji saya, saya mempunyai fungsi getTagQuotes yang mengandungi gelung for yang boleh membuat pelbagai panggilan API untuk mengembalikan data ke dalam satu tatasusunan.
Bagaimana kod saya untuk ini bermula di bawah:
// 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);
});
}
Fungsi getTagQuotes saya dengan pulangan janji:
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);
}
}
}
Perhatikan baris ini
return GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
ia berada di dalam gelung for saya:
for (var i=0; i<tags.length; i++)
Segalanya berfungsi dengan baik jika saya hanya perlu melakukan gelung sekali sahaja. Walau bagaimanapun, sebaik sahaja terdapat satu lagi tag (sehingga 3), ia masih hanya mengembalikan gelung/data pertama. Ia tidak menunggu sehingga gelung for selesai. Lalu mengembalikan janji. Jadi tweetArrayObjsContainer saya sentiasa hanya mempunyai tag pertama.
Jawapan #1
Tiga isu:
- anda tidak mengembalikan janji ditangguhkan dari kaedah
getTagQuotes. - anda sedang melihat
iuntuk melihat sama ada anda telah melalui gelung, dan gelung for sudah selesai (i == (tags.length - 1)) sebelum kejayaan pertama dipanggil. - Anda memanggil
returnpada iterasi pertama gelung tersebut sehingga anda tidak sempat sampai ke item kedua.
Berikut adalah kod yang diperbetulkan (belum diuji lagi)
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;
}
Jawapan #2
return deferred.promise; seharusnya menjadi nilai pulangan fungsi anda, bukan GetTweetVolFactory.returnTweetVol(), kerana itu adalah apa yang anda ingin jadikan janji (promisify).
Masalah anda ialah anda memanggil beberapa GetTweetVolFactory.returnTweetVol(), dan kemudian anda perlu menggabungkan semua panggilan async itu untuk menyelesaikan janji anda. Untuk melakukannya, anda harus menjadikan hanya satu panggilan GetTweetVolFactory.returnTweetVol() sebagai janji:
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;
}
Dan kemudian panggil setiap janji dalam satu gelung dan kembalikan janji yang diselesaikan apabila semua janji telah lengkap:
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);
}
Terdapat beberapa isu lagi dengan kod anda, tetapi anda sepatutnya dapat membuat ini berfungsi dengan tip saya.
