Mathias Lund Mathias Lund - 2 months ago 13
Node.js Question

Loop the object returned from node promise and feed to the next .then

I've been stuck with this problem for what seems to be an eternity.
I'm just getting into node, and starting to get my head around promises etc.

What I am trying to do is fetch data from the Spotify API, and the first thing I do is getting my own playlists:

function getPlaylists(access_token) {

var options = {
url: 'https://api.spotify.com/v1/me/playlists',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};

return new Promise(function(resolve, reject) {

request.get(options, function(error, response, body) {
var playlists = body.items;
var playlistArray = [];
playlists.forEach(function(playlist) {
var name = playlist.name;
var url = playlist.tracks.href;

playlistArray.push(url);
});

if(!error) {
resolve(playlistArray);
} else {
reject(error);
}

});

});
}


All right, so far so good. Now I also want to fetch the artists from these playlists:

function getArtists(url,access_token) {

var params = {
url: url,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};

return new Promise(function(resolve, reject) {

request.get(params, function(error, response, body) {

var tracks = body.items;
var artistArray = [];
tracks.forEach(function(artists) {
let allArtists = artists.track.artists;
allArtists.forEach(function(artist) {
artistArray.push(artist);
});
})

if(!error) {
resolve(artistArray);
} else {
reject(error);
}

});

})

}


The way I return all of this data is:

getPlaylists(access_token)
.then(function(playlists) {

playlists.forEach(function(playlist) {
getArtists(playlist,access_token)
.then(function(artist) {
return artist;
});
});


}).then(function(artists) {
console.log("getting artists",artists);
}).catch(function(error) {
console.log(error);
})


However, this returns undefined. I can only make it work if I pass in a single playlist url to the getArtists function - the problem is the forEach loop which I don't know how to handle.

Any help is greatly appreciated!

Answer

You can use a combination of [].map() and Promise.all() to make a Promise that waits for the completion of an array of actions:

getPlaylists(access_token)
  .then(playlists => Promise.all(playlists.map(playlist => 
    getArtists(playlist, access_token)))
  .then(artists => {
    // use here
  });

Remember, for Promises to chain you must return a value or a Promise from your .then() handler. .forEach() always returns undefined, no matter what.

First, we turn the array of playlist into an array of Promises by using .map() with getArtist, then we pass that array of Promises to Promise.all(), which returns a single Promise which resolves when all Promises in the array resolve (or rejects when the first rejects).