Lorsque la fonction ci-dessous se termine et fournit une liste finalisée des éléments du tableau "albums", je souhaite qu'elle appelle une autre fonction/fasse autre chose avec la liste.
Actuellement, il publie [] avant la fin de la fonction et je sais que cela est dû à une exécution asynchrone, mais je pensais que Node se lisait de manière linéaire car il s'agissait d'un seul thread?
function getAlbumsTotal(list, params){
for(var i = 0; i<list.length; i++){
api.getArtistAlbums(list[i], params).then(function(data) {
for(var alb = 0; alb<data.body.items.length; alb++){
albums.Push(data.body.items[alb].id);
}
}, function(err) {
console.error(err);
});
}
console.log(albums);
//do something with the finalized list of albums here
}
La fonction de rappel que vous fournissez à then
est en effet exécutée de manière asynchrone, ce qui signifie qu’elle n’est exécutée qu’après l’exécution du reste du code de la pile d’appels en cours, y compris votre console.log
final.
Voici comment vous pouvez le faire:
function getAlbumsTotal(list, params){
var promises = list.map(function (item) { // return array of promises
// return the promise:
return api.getArtistAlbums(item, params)
.then(function(data) {
for(var alb = 0; alb<data.body.items.length; alb++){
albums.Push(data.body.items[alb].id);
}
}, function(err) {
console.error(err);
});
});
Promise.all(promises).then(function () {
console.log(albums);
//do something with the finalized list of albums here
});
}
NB: Apparemment, albums
est défini comme une variable globale. Ce n'est pas une si bonne conception. Il serait préférable que chaque promesse fournisse son propre sous-ensemble d'albums et que l'appel Promise.all
soit utilisé pour concaténer ces résultats dans une variable locale. Voici à quoi cela ressemblerait:
function getAlbumsTotal(list, params){
var promises = list.map(function (item) { // return array of promises
// return the promise:
return api.getArtistAlbums(item, params)
.then(function(data) {
// return the array of album IDs:
return Array.from(data.body.items, function (alb) {
return alb.id;
});
}, function(err) {
console.error(err);
});
});
Promise.all(promises).then(function (albums) { // albums is 2D array
albums = [].concat.apply([], albums); // flatten the array
console.log(albums);
//do something with the finalized list of albums here
});
}
Si vous souhaitez utiliser les données renvoyées par une boucle dans node.js, vous devez ajouter un peu de code supplémentaire pour vérifier si vous en êtes à la dernière itération de la boucle. Fondamentalement, vous écrivez votre propre vérification "boucle complète" et ne courez que lorsque cette condition est vraie.
Je suis allé de l'avant et ai écrit un exemple complet et exécutable afin que vous puissiez le décomposer pour voir comment cela fonctionne. L'important est d'ajouter un compteur, de l'incrémenter après chaque boucle, puis de vérifier si le compteur a la même longueur que la liste sur laquelle vous effectuez une itération.
function getArtistAlbums(artist, params){
var artistAlbums = {
'Aphex Twin':['Syro', 'Drukqs'],
'Metallica':['Kill \'Em All', 'Reload']
};
return new Promise(function (fulfill, reject){
fulfill(artistAlbums[artist]);
});
}
function getAlbumsTotal(list, params){
var listCount = 0;
for(var i = 0; i<list.length; i++){
getArtistAlbums(list[i], params)
.then(function(data) {
listCount++;
for(var alb = 0; alb<data.length; alb++){
//for(var alb = 0; alb<data.items.length; alb++){
//albums.Push(data.body.items[alb].id);
albums.Push(data[alb]);
}
// print out album list at the end of our loop
if(listCount == list.length){
console.log(albums);
}
}, function(err) {
console.error(err);
});
}
// prints out too early because of async nature of node.js
//console.log(albums);
}
var listOfArtists = ['Aphex Twin', 'Metallica'];
var albums = [];
getAlbumsTotal(listOfArtists, 'dummy params');