多个异步调用回调
Multiple async calls callback
我在处理多个异步调用时遇到问题。我有三个任务要申请。首先,我从第一个请求中获得了一些 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 发出所有请求
使用 promise 对数组上的一系列操作进行排序的一种常见方法是 .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()
处理程序内部,它 returns MovieService.getContent()
承诺,以便此迭代也将等待内部承诺完成。
您也可以并行执行这些操作,而不必强制对它们进行排序。您只需要知道它们何时全部完成,并且您需要按顺序保持所有数据。可以这样做:
// 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()
上的错误来实现,以便它总是 returns 已解决的承诺。
您可以只 运行 异步地承诺,而不是链接承诺,当所有承诺都得到解决时,无论其中任何一个是否失败,您可以使用您的 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。
我在处理多个异步调用时遇到问题。我有三个任务要申请。首先,我从第一个请求中获得了一些 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 发出所有请求
使用 promise 对数组上的一系列操作进行排序的一种常见方法是 .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()
处理程序内部,它 returns MovieService.getContent()
承诺,以便此迭代也将等待内部承诺完成。
您也可以并行执行这些操作,而不必强制对它们进行排序。您只需要知道它们何时全部完成,并且您需要按顺序保持所有数据。可以这样做:
// 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()
上的错误来实现,以便它总是 returns 已解决的承诺。
您可以只 运行 异步地承诺,而不是链接承诺,当所有承诺都得到解决时,无论其中任何一个是否失败,您可以使用您的 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。