Search code examples
javascriptdynamicpromisebluebirdsequential

Parallel promise chain with sequential dynamic content in between


I can't get my head around this problem how dynamic results can be added to a promise chain, i've looked everywhere if this is even possible. Hope someone can clarify or give me a good solution for the following example i'm trying to accomplish (it's a small music player):

// API requests are done with a promise
let pfetch = (url) => Promise.resolve( $.ajax(...) )

// This will give me all playlists from a user
pfetch( 'USER-API-URL' )

    // The playlists i get have tracklists
    .then( results =>

        // When all playlists are fetched continue (Promise.all = parallel)
        Promise.all( results.playlists.map( playlist => pfetch(playlist.url) ) )
    )

    // Results of all playlists in arguments
    .then( playlists => 

        // Each playlist has tracks and next_url
        playlists.map( playlist => {

        /* playlist.tracks 
         * if (playlist.next_url) pfetch(playlist.next_url)
         * append results to playlist.tracks
         */

        })
    )

        //only continue if all all tracks are complete

    //can i do a Promise.join() in the previous step?
    .then( () => console.log('finished,yay!') )

Some of the track lists have a next_url in their response, meaning, i should also fetch the next url belonging to the track list, so i can get a complete tracklist.tracks. However this has to be sequential as i don't know if there is a subsequent next_url in the response. When there is no next_url i'm finished with one track list and can do the same with the next (hence .map).

So my question, how do you do this dynamic content chaining in promises? I know you can do sequencing with reduce, but in this case the amount of next_url's is not fixed. I've thought about recursion and yet to find a good example for Promise.each.

Thumbs up in advance.


Solution

  • If you create a function to recursively deal with a playlist url

    var getPlaylistUrl = url => 
        pfetch(url).then(playlist => 
            playlist.next_url ? Promise.all([].concat.apply([playlist.tracks], getPlaylistUrl(playlist.next_url))) : playlist.tracks
        );
    

    you should be able to rewrite your code as follows

    pfetch('USER-API-URL')
    .then(results => Promise.all(results.playlists.map(playlist => getPlaylistUrl(playlist.url))))
    .then( playlists => {
        // playlists should be complete
    })
    .then( () => console.log('finished,yay!') );