多个异步调用回调

时间:2015-12-13 17:11:49

标签: javascript asynchronous es6-promise

我遇到多个异步调用问题。我有三个我想申请的任务。首先,我从第一次请求获得一些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女巫提出所有请求

2 个答案:

答案 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