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
- Check if playlist exists. If playlist doesn't exist, create it.
GET
recommended tracks- Add recommended tracks to playlist
Normally, 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
- How would one write a sequential execution order like this using observables?
- Why is the function
getRecommendedTracks
only executed if I don't let the functionaddRecommendedTracksToPlaylist
run in the 'chain'? - Have I completely misunderstood the
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.