React-Redux Javascript应用程序 - 尝试按顺序激活大量API调用

时间:2016-09-14 20:02:49

标签: javascript jquery ajax react-redux

我遇到了对两个不同API的连续API调用(使用JQuery的AJAX)的问题,以便构建具有特定属性的对象。这是我的应用程序的摘要以及我正在尝试做的事情:

用户输入演员或导演的名称,该应用程序将返回总共五部电影,每部电影都具有某些属性,如标题,概述,年份,预算,收入以及指向YouTube预览。我正在使用The Movie Database API,以及用于YouTube链接的YouTube API。

以下是当前工作方式的顺序,所有这些都发生在Redux应用的动作创建者中:

  1. 演员姓名被发送到TMDB API - 返回ActorID号
  2. ActorID号码被发送到TMDB API - 返回20部电影:标题,概述,年份,海报链接和MovieID号码
  3. 对于该列表中的每部电影,MovieID号码会被发送到API - 返回更多属性:预算,收入和IMDB-ID(稍后在链接中使用)
  4. 同样对于第2步中的每部电影,标题会发送到YouTube API - 返回预览链接。
  5. 一旦为每部电影组装了所有这些信息,我想返回前五部电影并将其作为动作有效负载发送到Redux商店。

    我正在使用一些承诺,我已经尝试了重新排列功能流程所能想到的一切,但是只需点击一下提交按钮,我就无法获得所需的所有信息。有趣的是,它可以通过两次点击提交按钮来实现,我想是因为到那时所有的异步AJAX调用终于完成了。但是在第一次单击之后,我有一个空数组,其中应该有电影对象。

    以下是一些代码,应该总结一下这些内容:

    var personId
    var movies = []
    
    function actorByRating(UserInput) {
    

    第1步:获取演员ID号:

    function searchActors() {
    return $.ajax({
      method: "GET",
      url: `https://api.themoviedb.org/3/search/person?query=${UserInput}&api_key=<key>`
    }).done(function(response){
      personId = response.results[0].id
    })
    }
    

    步骤2:使用Actor ID获取电影列表,开始为它们分配属性:

    function getMovies() {
    $.ajax({
      method: "GET",
      url: `https://api.themoviedb.org/3/discover/movie?with_cast=${personId}&vote_count.gte=20&sort_by=vote_average.asc&budget.desc&api_key=<key>&include_image_language=en`
    }).done(function(response) {
      response.results.forEach((m) => {
        var movie = {}
        movie.title = m.title
        movie.year = m.release_date.split("-")[0]
        movie.movieId = m.id
        movie.overview = m.overview
        movie.poster = "http://image.tmdb.org/t/p/w500" + m.poster_path
    
        getMovieInfo(movie) //step 3
        getYouTube(movie) //step 4
        saveMovie(movie)
      })
    })
    }
    function saveMovie(movie){
      movies.push(movie)
    }
    

    第3步功能,将电影对象作为参数:

    function getMovieInfo(m){
      return $.ajax({
        method: "GET",
        url: `https://api.themoviedb.org/3/movie/${m.movieId}?&api_key=<key>&append_to_results=imdb_id`
      }).done(function(response) {
        m.revenue = response.revenue
        m.budget = response.budget
        m.imdbId = response.imdb_id
      })
    }
    

    第4步功能,获取Youtube链接。还拍摄电影对象:

    function getYouTube(movie){
      $.ajax({
      method: "GET",
      url: `https://www.googleapis.com/youtube/v3/search?part=snippet&q=${movie.title.split(" ").join("+")}+trailer&key=<key>`
      }).done(function(yt){
        movie.youtubeLink = `http://www.youtube.com/embed/${yt.items[0].id.videoId}`
      })
    }
    

    在此之后,当过滤功能有一系列可以使用的电影时,过滤功能可以正常工作。问题是,我认为,所有这些连续的API调用在前一个API调用完成之前会继续触发,而后者需要来自早期API调用的信息才能进行搜索。因此,当我第一次单击提交时,最终的电影数组为空,因此调度的有效负载是一个空数组。然后电影对象被填写,所以当你再次点击提交时,电影已经在那里工作了,应用程序的其余部分工作正常。

    我已经尝试了所有我能想到的东西来减慢进程,连锁承诺(这不起作用,因为第2步必须运行几部电影,即每个功能的返回值不断变化,所以我可以“t”。然后“他们”,重新组织进来的信息......但是我无法通过过滤功能实际运行以创建适当的有效负载时为我提供我需要的所有属性的电影对象。

    非常感谢任何帮助或建议!

    (注意:上面的“关键”内容只是占位符文本)

    更新: 我将代码更改为基本以下内容:

    searchActors()
    .then(function(response){
    const actorId = response.data.results[0].id
    return actorId
    })
    .then((personID) => {
    return getMoviesFromPersonID(personID)
    })
    .then(function(response) {
    const movieList = []
    response.data.results.forEach((m) => {
      var movie = {}
      movie.title = m.title
      movie.year = m.release_date.split("-")[0]
      movie.movieId = m.id
      movie.overview = m.overview
      movie.poster = "http://image.tmdb.org/t/p/w500" + m.poster_path
      movieList.push(movie)
      // saveMovie(movie)
    })
    return Promise.all(movieList)
    })
    .then((movieList) => {
    const deepMovieList = []
    movieList.forEach((movie) => {
      getMovieInfo(movie)
      .then(function(response) {
        movie.revenue = response.data.revenue
        movie.budget = response.data.budget
        movie.imdbId = response.data.imdb_id
        deepMovieList.push(movie)
      })
    })
    return Promise.all(deepMovieList)
    })
    .then((deepMovieList) => {
    const finalMovies = []
    deepMovieList.forEach((movie) => {
      finalMovies.push(getYouTube(movie))
    })
    return Promise.all(finalMovies)
    })
    

    在第一次提到“deepMovieList”之前,一切正常。我似乎无法弄清楚如何让这个特定的步骤正常工作,因为它主要涉及与movieList中的每个电影进行20次API调用。我无法弄清楚如何1)从API获取信息,2)将属性分配给传入getMovieInfo的电影对象,然后3)将该电影对象(具有新属性)推送到我可以使用Promise.all的数组,所有这些都不会中断promise链。

    要么过早地移动到下一个“then”函数(而deepMovieList仍然是一个空数组),或者,对于我尝试过的其他随机内容,数组最终都是未定义的。

    如何让下一个“then”函数等到20个API调用并且每个电影对象都有其更新的属性?对于YouTube链接,这也会在下一步遇到同样的问题。

    谢谢!

1 个答案:

答案 0 :(得分:0)

TL; DR:使用Promise.all并使用searchActor()承诺而不是jQuery,组承诺。

更长的版本

好的,我不打算在这里重复你的所有代码。我会缩写一些东西以保持简单。

基本上,您需要完成许多任务。我假装他们每个人都是一个函数,它返回一个使用你想要的数据解决的承诺。

  1. getMoviesFromActorID(actorId) - 返回使用某个ID号
  2. 解决的承诺
  3. getMovie(movieId) - 返回使用电影ID数组解析的承诺
  4. getYoutube(movie) - 返回使用给定电影ID的详细信息解析的承诺
  5. // search for an actor searchActor('Brad Pitt') // then get the movie IDs for that actor .then((actorId) => getMoviesFromActorID(actorId)) // then iterate over the list of movie IDs & build an array of // promises. Use Promise.all to create a new Promise which is // resolved when all are resolved .then((movieIdList) => { const promiseList = []; movieIdList.forEach((id) => promiseList.push(getMovie(id))); return Promise.all(promiseList); }) // then get Youtube links for each of the movies .then((movieDetailsList) => { const youtubeList = []; movieDetailsList.forEach((movie) => youtubeList.push(getYoutube(movie))); return Promise.all(youtubeList); }) // then do something with all the information you've collected .then((finalResults) => { // do something interesting... }); - 返回使用Youtube嵌入代码解析的承诺。
  6. 鉴于这种基本设置(我承认我遗漏了很多东西),代码如下所示:

    Promise.all

    关键是Promise.alldocumentation here),它将获取Promises数组(或包含promises的任何其他可迭代对象)并创建一个新的Promise,它将解析所有原始的承诺已经解决。通过使用<%= render :partial => 'controller/partial', :collection => @objects %> ,您可以在您的承诺链中创建一个步骤,其中可以包含可变数量的并行操作,这些操作必须在下一步之前完成。

    可以做这样的事情,jQuery和回调,但它会非常难看。承诺的一大好处是能够布置如上所述的一系列步骤。