我遇到多个异步调用问题。我有三个我想申请的任务。首先,我从第一次请求获得一些json数据。然后,当该请求完成后,我向另一个服务的getMovie预告片发出多个请求,并将该数据合并到第一个响应中的每个对象。当所有这一切都完成后,我想将该数据写入json文件。很简单,但我没有兑现承诺。
这是我的代码
MovieService.getContent(config.url + '?key=' + config.moviekey).then(function(data) {
var movies = JSON.parse(data);
var cnt = 0;
var len = movies.length;
var results = [];
for (var i = 0; i < movies.length; i++) {
var imdbid = movies[i].ids.imdb;
(function (i, imdbid) {
MovieService.getContent(config.themoviedburl + 'tt' + imdbid + '/videos?api_key=' + config.themoviedbkey).then(function (data) {
len--;
movies[i].trailers = JSON.parse(data);
if (len === 0) {
FileService.writeToJson(JSON.stringify(movies), config.showtimesfilepath);
}
});
})(i, imdbid);
};
});
现在这可以工作但是如果循环中的一个请求失败会怎样。然后我的计数器不正确,我会将数据写入文件。有人可以请帮助我使用promises设置类似的场景。哦,是的,我的代码的另一个特点是MovieService女巫提出所有请求
答案 0 :(得分:2)
使用promises对数组上的一系列操作进行排序的常用方法是使用.reduce()
,其中每次迭代都会添加到promise链上,从而导致所有异步操作都被排序:
// 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);
});
});
从概念上讲,这样做的是调用movies.reduce()
,传递给.reduce()
的起始值是一个已解决的承诺。然后,通过.reduce()
的每次迭代都会添加到p = p.then(...)
之类的承诺链上。这会导致所有操作都被排序,等待一个操作完成,然后再调用下一个操作。然后,在.then()
处理程序内部,它返回MovieService.getContent()
promise,以便此迭代将等待内部promise也完成。
您也可以并行执行这些操作,而不必强制对它们进行排序。您只需知道它们何时完成,您需要保持所有数据的顺序。这可以这样做:
// 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);
});
});
或者,使用Bluebird的promise库有用的Promise.map()
,这是一个较短的并行版本
// 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);
});
});
如果您希望即使任何给定的请求失败也要继续进程,那么您必须描述在这种情况下您希望发生什么,并且您可以通过处理.getContent()
上的错误来完成此操作,以便它始终返回一个已经解决的承诺。
答案 1 :(得分:0)
您可以异步运行promises而不是链接promises,并且当所有promise都被解析时,无论其中任何一个是否失败,您都可以使用FileService写入JSON。这是通过使用Promise.all()
完成的。
例如,
MovieService.getContent(config.url + '?key=' + config.moviekey).then(function (data) {
var movies = JSON.parse(data);
var movieDataPromises = [];
movies.forEach(function (movie) {
var imdbid = movie.ids.imdb;
movieDataPromises.push(
MovieService.getContent(config.themoviedburl + 'tt' + imdbid + '/videos?api_key=' + config.themoviedbkey).then(function (trailerData) {
movie.trailers = JSON.parse(trailerData);
}).catch(function () {});
);
});
Promise.all(movieDataPromises).then(function () {
FileService.writeToJson(JSON.stringify(movies), config.showtimesfilepath);
});
});
检索预告片信息时有一个空回调体的捕获的原因是因为我们想阻止Promise.all()
执行其快速失败行为。
编辑:避免使用promise constructor antipattern。