Reputation: 8879
Exploring observables, I've run into a problem where I need to execute functions only after another function has been completed. All functions are async operations that use the Http
service in @angular/http
.
The call chain should be
GET
recommended tracksNormally, I would've written this with promises and have a chain that would look something like
this.checkIfPlaylistExists()
.then((exists) => {
if(!exists) {
return this.createPlaylist();
}
})
.then(() => this.getRecommendedTracks(seedTracks)) //resolves tracks
.then(this.addRecommendedTracksToPlaylist)
.then(doSomethingElse)
.catch();
My confusion is how I would write the code using observables instead. What I have so far isn't working 100%, and looks like this
createRecommendedTracksPlaylist(seedTracks: Track[]) {
this.checkIfPlaylistExists()
.map(exists => {
if (!exists) {
return this.createPlaylist();
}
})
.map(() => this.getRecommendedTracks(seedTracks))
.map((tracks) => this.addRecommendedTracksToPlaylist(tracks)) //<--- if I comment out this line, 'getRecommendedTracks' is executed and prints out response
.mergeAll()
.subscribe();
}
The functions called
getRecommendedTracks(seedTracks: Track[]) {
seedTrackIds = seedTracks.map((track) => track.id).join(',');
const recommendationsUrl = `https://api.spotify.com/v1/recommendations?seed_tracks=${seedTrackIds}`;
return this.http.get(recommendationsUrl, options).map((res) => {
const recommendedTracks = res.json().tracks as Array<Track>;
console.log('Getting recommended tracks'); //Is only run if I don't run 'addRecommendedTracksToPlaylist' function
console.log(recommendedTracks);
return recommendedTracks;
});
}
addRecommendedTracksToPlaylist(tracksToAdd) {
console.log('Going to add tracks..');
console.log(tracksToAdd); //Prints out 'Observable {_isScalar: false, ... }'
const options = this.getAuthHeader();
const playlistUri = '2OGx5l1ItjzMsQdQ0Hec6g';
const addTracksUrl = `https://api.spotify.com/v1/users/${this.userId}/playlists/${playlistUri}/tracks`;
return this.http.get(addTracksUrl, options);
}
Edit: Adding the checkIfPlaylistExists
code (please not that the playlist currently always exists)
private checkIfPlaylistExists() {
const options = this.getAuthHeader();
const playlistUrl = `https://api.spotify.com/v1/users/${this.userId}/playlists`;
return this.http.get(playlistUrl, options).map((res) => {
const playlists = res.json().items as Array<any>;
return playlists.some((e, i, a) => {
return e.name.indexOf(this.playlistTitle) > -1;
})
});
}
So to sum it up
getRecommendedTracks
only executed if I don't let the function addRecommendedTracksToPlaylist
run in the 'chain'?Observable.map
function? All examples I've looked at use it for single objects as well as arrays, but I'm not sure I'm using it correctly.Upvotes: 3
Views: 1468
Reputation: 8468
Just change your .map()
to.flatMap()
and you are good to go:
createRecommendedTracksPlaylist(seedTracks: Track[]) {
this.checkIfPlaylistExists()
.flatMap(exists => {
if (!exists) {
return this.createPlaylist();
}
return Observable.of(1);
})
.flatMap(() => this.getRecommendedTracks(seedTracks))
.flatMap((tracks) => this.addRecommendedTracksToPlaylist(tracks))
.subscribe();
}
You will need to return a dummy Observable if the playlist
doesnt exist, because flatMap expect a return of Observable. Observable.empty()
won't work because it just end the stream.
Upvotes: 2