There are 2 functions that I need to run one-by-one: getUserPlaylists
(receives Playlists) and getPlaylistTracks
(receives Tracks for provided Playlist).
One response can have up to 50 tracks, so I need to use PageToken
if I want to get the rest tracks. The problem is that I can not make a recursive function getPlaylistTracks
to wait till the recursion is done.
function getPlaylistsWithTracks () {
return new Promise((resolve, reject) => {
getUserPlaylists()
.then(function (playlists) {
playlists.forEach(
async function (playlistObj) {
await getPlaylistTracks(playlistObj).then(function (tracks) {
playlistObj['tracks'] = tracks
})
})
console.log('resolve')
resolve(playlists)
})
})
}
function getPlaylistTracks (playlistObj, pageToken) {
return new Promise((resolve, reject) => {
let playlistTracks = []
let requestOptions = {
'playlistId': playlistObj['youtubePlaylistId'],
'maxResults': '50',
'part': 'snippet'
}
if (pageToken) {
console.log('pageToken:', pageToken)
requestOptions.pageToken = pageToken
}
let request = gapi.client.youtube.playlistItems.list(requestOptions)
request.execute(function (response) {
response['items'].forEach(function (responceObj) {
let youtubeTrackTitle = responceObj.snippet.title
if (youtubeTrackTitle !== 'Deleted video') {
let youtubeTrackId = responceObj.snippet.resourceId.videoId
playlistTracks.push({
youtubePlaylistId: playlistObj.playlistId,
youtubePlaylistTitle: playlistObj.playlistTitle,
youtubeTrackId: youtubeTrackId,
youtubeTrackTitle: youtubeTrackTitle,
})
}
})
// Here I need to wait a bit
if (response.result['nextPageToken']) {
getPlaylistTracks(playlistObj, response.result['nextPageToken'])
.then(function (nextPageTracks) {
playlistTracks = playlistTracks.concat(nextPageTracks)
})
}
})
resolve(playlistTracks)
})
}
getPlaylistsWithTracks()
In my case in console I see the next:
> resolve
> pageToken: 123
> pageToken: 345
but, I want to see resolve
the last.
How to wait till recursion is executed?
Avoid the Promise
constructor antipattern, and (don't) use forEach
with async
functions properly.
Furthermore, there is nothing special about recursion. It's like any other promise-returning function call that you would want to wait for - put it in your then
chain or await
it. (The latter is considerably easier).
async function getPlaylistsWithTracks() {
const playlists = await getUserPlaylists();
for (const playlistObj of playlists) {
const tracks = await getPlaylistTracks(playlistObj);
playlistObj.tracks = tracks;
}
console.log('resolve')
return playlists;
}
async function getPlaylistTracks(playlistObj, pageToken) {
let playlistTracks = []
let requestOptions = {
'playlistId': playlistObj['youtubePlaylistId'],
'maxResults': '50',
'part': 'snippet'
}
if (pageToken) {
console.log('pageToken:', pageToken)
requestOptions.pageToken = pageToken
}
let request = gapi.client.youtube.playlistItems.list(requestOptions)
const response = await new Promise((resolve, reject) => {
request.execute(resolve); // are you sure this doesn't error?
});
response['items'].forEach(function (responceObj) {
let youtubeTrackTitle = responceObj.snippet.title
if (youtubeTrackTitle !== 'Deleted video') {
let youtubeTrackId = responceObj.snippet.resourceId.videoId
playlistTracks.push({
youtubePlaylistId: playlistObj.playlistId,
youtubePlaylistTitle: playlistObj.playlistTitle,
youtubeTrackId: youtubeTrackId,
youtubeTrackTitle: youtubeTrackTitle,
})
}
})
if (response.result['nextPageToken']) {
const nextPageTracks = await getPlaylistTracks(playlistObj, response.result['nextPageToken']);
playlistTracks = playlistTracks.concat(nextPageTracks);
}
return playlistTracks;
}