简体   繁体   English

多个异步调用回调

[英]Multiple async calls callback

I'm having problem with multiple async calls. 我有多个异步调用问题。 I have three tasks i want to apply. 我有三个要申请的任务。 First i get some json data from first request. 首先,我从第一个请求中获取了一些json数据。 Then when that request has finished I make multiple request to getMovie trailers from another service and merge that data to every object in the first response. 然后,当该请求完成时,我向另一个服务请求了多个getMovie预告片,并将该数据合并到第一个响应中的每个对象。 When all that has finished then i want to write that data to json file. 当所有这些都完成后,我想将该数据写入json文件。 Simple enough but I'm failing at promises. 很简单,但是我没有兑现承诺。

Here's my code 这是我的代码

    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);
    };
});

Now this works but what if one of the request in the loop would fail. 现在这可以工作,但是如果循环中的请求之一失败了该怎么办。 Then my counter is incorrect and I would write the data to file. 然后我的计数器不正确,我会将数据写入文件。 Could someone please help me set up similar scenario using promises. 有人可以帮助我使用诺言建立类似的情况。 And ohh yes another pease of my code is the MovieService witch make all the requests 哦,是的,我的代码的另一个特点是MovieService女巫发出了所有请求

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: 使用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);
    });
});

Conceptually, what this does is call movies.reduce() and the starting value passed into .reduce() is a resolved promise. 从概念上讲,这样做是调用movies.reduce()并传递到初始值.reduce()是一个解决的承诺。 Then each iteration through .reduce() adds onto the promise chain with something like p = p.then(...) . 然后,通过.reduce()每次迭代都.reduce()类似p = p.then(...)添加到promise链中。 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. 然后,在该MovieService.getContent() .then()处理函数内部,它返回MovieService.getContent()承诺,以便此迭代也将等待内部承诺完成。


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 或者,使用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);
    });
});

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. 如果您希望即使给定请求失败,该过程也可以继续进行,则必须描述在这种情况下要发生的事情,并且可以通过处理.getContent()上的错误来完成此操作,以便它始终返回已解决的.getContent()

Instead of chaining the promises, you can just run the promises asynchronously, and when all promises are resolved whether or not any one of them fails, you can write to JSON using your FileService. 无需链接承诺,您可以仅异步运行承诺,并且在所有承诺均得到解决(无论其中一个是否失败)后,可以使用FileService写入JSON。 This is done by using Promise.all() . 这是通过使用Promise.all()

For example, 例如,

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);
  });
});

The reason why there is a catch with an empty callback body when retrieving the trailer info is because we want to prevent the Promise.all() from executing its fail-fast behavior. 检索预告片信息时使用空的回调主体捕获内容的原因是因为我们要防止Promise.all()执行其快速失败行为。

EDIT: avoid using the promise constructor antipattern . 编辑:避免使用promise构造函数antipattern

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM