web-dev-qa-db-fra.com

Node - Attend la fin de la boucle?

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
}
6
Ralph

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
    });
}
8
trincot

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');
0
Jim Factor