1

I've been trying to figure out what's the correct way or the best practice in terms of working with Async requests and observables, but I cannot seem to find something that is straightforward. Also, there's a lot of stuff out there that suggest using Promises instead of Observables, but I read that Observables are much more flexible to deal with. So I want to continue working with Observables.

So basically, the functions in my service class looks like this.

getUserTop(spotifyRequestType, timeRange, resultLimit): Observable<any>{
    return this.httpClient.post<Observable<any>>('http://localhost:3000/getUserTop', {
      spotifyRequestType: spotifyRequestType, //artist or track
      timeRange: timeRange, //between short_term, medium_term, long_term
      resultLimit: resultLimit //number of results to be displayed
    }).pipe(catchError(this.handleError));
  }

  getRecommendations(seed_artists):Observable<any> {
      return this.httpClient.post<Observable<any>>('http://localhost:3000/getRecommendations', 
    {
      seed_artists : seed_artists
    }
    ).pipe(catchError(this.handleError));  
  }

 createPlaylist(playlistName, playlistDesc, isPublic, tracks): Observable<any>{
    return this.httpClient.post<Observable<any>>('http://localhost:3000/createPlaylist', {
      playlistName: playlistName, //name of the playlist
      playlistDesc: playlistDesc,  //desc of the playlist
      isPublic: isPublic, //public or private indicator
      tracks: tracks //tracks to be added to the playlist
    }).pipe(catchError(this.handleError));
  }

I'm running an Node.js server on localhost:3000. The get functions simply get JSON objects from the Node

  1. Get top artists.
  2. Get recommended tracks based on artists.
  3. Create playlist and add tracks.

These are my methods.

getTopArtists(spotifyRequestType, timeRange, resultLimit): String {
    var artistList = '';
    this.spotifyService.getUserTop(spotifyRequestType, timeRange, resultLimit).subscribe(
      data => {
        for(var i=0; i<data['items'].length; i++ ){
          artistList += data['items'][i]['id'];
        }  
      }
    ) 
    return artistList;
  }

  getRecommendations(artist): String[]{
    var trackList = [];
    this.spotifyService.getRecommendations(artist).subscribe(
      data => {
        for(var i=0; i<data['items'].length; i++ ){
          trackList.push(data['items'][i]['id']); 
        }  
      }
    ) 
    return trackList;    
  }

  addRecommendationsToPlaylist(playlistName, playlistDescription, isPublic, tracks){
    this.spotifyService.createPlaylist(playlistName, playlistDescription, isPublic, tracks).subscribe(
      data => {
        console.log(data);
      })

  }

  publishPlaylistToSpotify(){
    var topArtists = this.getTopArtists('artists', 'short_term', '5' ); //Get a user's recent top 5 artists.
    var trackList = this.getRecommendations(topArtists); //Get a list of tracks based on top artists.
    this.addRecommendationsToPlaylist('My Playlist', 'My Playlist Description', 'false', trackList); //Create a playlist and add tracks to it.
  }

The way it stands, since the methods contain async requests, the publishPlaylistToSpotify() won't work as execution doesn't wait for topArtists and trackList to be fully fetched. In my publishPlaylistToSpotify(), how do I make sure that first topArtists is fully fetched, then trackList is fully fetched and then move on to creating the playlist?

user2827224
  • 61
  • 2
  • 6
  • https://stackoverflow.com/questions/37748241/how-to-do-the-chain-sequence-in-rxjs/37748799#37748799 – JGFMK Feb 27 '19 at 18:55

1 Answers1

0

getTopArtists and also getRecommendations make async calls for data. They should thus also return a promise or observable. See the suggested duplicate on how to work with asynchronous calls in javascript.

For users starting out with asynchronous calls in javascript I recommend using async/await because the resulting code resembles synchronous calls. See your code below refactored and applying async/await.

async getTopArtists(spotifyRequestType, timeRange, resultLimit): Promise<String> {
    var artistList = '';
    var data = await this.spotifyService.getUserTop(spotifyRequestType, timeRange, resultLimit).toPromise();
    for(var i=0; i<data['items'].length; i++ ){
      artistList += data['items'][i]['id'];
    }
    return artistList;
}

async getRecommendations(artist): Promise<String[]> {
    var trackList = [];
    var data = await this.spotifyService.getRecommendations(artist).toPromise();
    for(var i=0; i<data['items'].length; i++ ){
      trackList.push(data['items'][i]['id']); 
    }  
    return trackList;
}

addRecommendationsToPlaylist(playlistName, playlistDescription, isPublic, tracks){
    this.spotifyService.createPlaylist(playlistName, playlistDescription, isPublic, tracks)
        .subscribe(data => { console.log(data); });
}

async publishPlaylistToSpotify() {
    var topArtists = await this.getTopArtists('artists', 'short_term', '5' ); //Get a user's recent top 5 artists.
    var trackList = await this.getRecommendations(topArtists); //Get a list of tracks based on top artists.
    this.addRecommendationsToPlaylist('My Playlist', 'My Playlist Description', 'false', trackList); //Create a playlist and add tracks to it.
}
Igor
  • 60,821
  • 10
  • 100
  • 175
  • Thank you so much for this! I had read a bit about async/await but found out that it could only be used with Promises and not observables. I looked at the other answers and also saw that rxjs provides a forkJoin() method that can be used to achieve something similar with Observables as well. I will research a bit on using forkJoin as well. Again, thank you for your help! – user2827224 Feb 27 '19 at 19:23
  • @user2827224 - glad to help. Please consider marking an answer using the checkmark on the left if you feel it resolved your question. – Igor Feb 27 '19 at 19:27