多个异步调用回调

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