A common way to sequence a series of operations on an array using promises is with .reduce()
where each iteration adds onto a promise chain causing all the async operations to be sequenced:
// in sequence
MovieService.getContent(config.url + '?key=' + config.moviekey).then(function(data) {
var movies = JSON.parse(data);
return movies.reduce(function(p, movie, index) {
return p.then(function() {
var imdbid = movie.ids.imdb;
return MovieService.getContent(config.themoviedburl + 'tt' + imdbid + '/videos?api_key=' + config.themoviedbkey).then(function (data) {
movies[index].trailers = JSON.parse(data);
}, function(err) {
// handle the error here and decide what should be put into movies[index].trailers
});
});
}, Promise.resolve()).then(function() {
return FileService.writeToJson(JSON.stringify(movies), config.showtimesfilepath);
});
});
Conceptually, what this does is call movies.reduce()
and the starting value passed into .reduce()
is a resolved promise. Then each iteration through .reduce()
adds onto the promise chain with something like p = p.then(...)
. This causes all the operations to be sequenced, waiting for one to complete before invoking the next. Then, inside of that .then()
handler, it returns the MovieService.getContent()
promise so that this iteration will wait for the inner promise to complete too.
You can probably do these operations in parallel too without forcing them to be sequenced. You just need to know when they are all done and you need to keep all the data in order. That could be done like this:
// in parallel
MovieService.getContent(config.url + '?key=' + config.moviekey).then(function(data) {
var movies = JSON.parse(data);
var promises = [];
movies.forEach(function(movie, index) {
var imdbid = movie.ids.imdb;
promises.push(MovieService.getContent(config.themoviedburl + 'tt' + imdbid + '/videos?api_key=' + config.themoviedbkey).then(function (data) {
movies[index].trailers = JSON.parse(data);
}, function(err) {
// handle the error here and decide what should be put into movies[index].trailers
}));
});
Promise.all(promises).then(function() {
return FileService.writeToJson(JSON.stringify(movies), config.showtimesfilepath);
});
});
Or, using the Bluebird's promise library's helpful Promise.map()
, here's a shorter parallel version
// use Bluebird's Promise.map() to run in parallel
MovieService.getContent(config.url + '?key=' + config.moviekey).then(function(data) {
var movies = JSON.parse(data);
Promise.map(movies, function(movie, index) {
var imdbid = movie.ids.imdb;
return MovieService.getContent(config.themoviedburl + 'tt' + imdbid + '/videos?api_key=' + config.themoviedbkey).then(function (data) {
movies[index].trailers = JSON.parse(data);
}, function(err) {
// handle the error here and decide what should be put into movies[index].trailers
});
}).then(function() {
return FileService.writeToJson(JSON.stringify(movies), config.showtimesfilepath);
});
});
If you want the process to continue even if any given request fails, then you have to describe what you want to happen in that case and you can accomplish that by handling an error on .getContent()
so that it always returns a resolved promise.