【问题标题】:Axios: chaining multiple API requestsAxios:链接多个 API 请求
【发布时间】:2017-10-26 05:57:38
【问题描述】:

我需要链接来自 Google Maps API 的一些 API 请求,我正在尝试使用 Axios 来完成。

这里是第一个请求,在componentWillMount()中

axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
  .then(response => this.setState({ p1Location: response.data }))  }

这是第二个请求:

axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2)
  .then(response => this.setState({ p2Location: response.data }))

然后我们有第三个请求,这取决于前两个是否完成:

axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + this.state.p1Location.results.place_id + '&destination=place_id:' + this.state.p2Location.results.place_id + '&key=' + 'API-KEY-HIDDEN')
  .then(response => this.setState({ route: response.data }))

如何链接这三个调用,以便第三个调用发生在前两个调用之后?

【问题讨论】:

    标签: reactjs react-native axios


    【解决方案1】:
    1. 对于Axios的并发请求,可以使用Axios.all()
    2. 在第一个和第二个之后使用.then()处理以下API请求。
    3. 使用axios.spread() 将参数数组展开为多个参数。
    4. 获取构建新 URL 所需的数据。
    5. 发出第三个 API 请求。
    6. 再次使用 .thenuseState() 并获取所需的所有数据。
    let urlOne = "", urlTwo = ""; // URL's API
    
    axios.all([
      axios.get(urlOne);
      axios.get(urlTwo);
    ]).then(
      axios.spread((respOne, respTwo) => {
         let idOne = respOne.data.results.place_id, 
             idTwo = respTwo.data.results.place_id,
             urlThree = ""; // Build your third URL here
         axios.get(urlThree)
           .then((respThree) => {
               // You have all the data available here to useState()
            })
      })
    )
    

    【讨论】:

      【解决方案2】:

      聚会有点晚了,但我喜欢这种链接承诺的模式,返回它们以保持承诺链的活力。

      axios
        .get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
        .then(response => {
          this.setState({ p1Location: response.data });
          return axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2);
        })
        .then(response => {
          this.setState({ p2Location: response.data });
          return axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p3);
        })
        .then(response => {
          this.setState({ p3Location: response.data });
        }).catch(error => console.log(error.response));
      

      【讨论】:

      【解决方案3】:

      创建承诺数组,然后使用reduce

      /**
       * Runs promises from array of functions that can return promises
       * in chained manner
       *
       * @param {array} arr - promise arr
       * @return {Object} promise object
       */
      function runPromiseInSequence(arr, input) {
        return arr.reduce(
          (promiseChain, currentFunction) => promiseChain.then(currentFunction),
          Promise.resolve(input)
        )
      }
      
      // promise function 1
      function p1(a) {
        return new Promise((resolve, reject) => {
          resolve(a * 5)
        })
      }
      
      // promise function 2
      function p2(a) {
        return new Promise((resolve, reject) => {
          resolve(a * 2)
        })
      }
      
      // function 3  - will be wrapped in a resolved promise by .then()
      function f3(a) {
       return a * 3
      }
      
      // promise function 4
      function p4(a) {
        return new Promise((resolve, reject) => {
          resolve(a * 4)
        })
      }
      
      const promiseArr = [p1, p2, f3, p4]
      runPromiseInSequence(promiseArr, 10)
        .then(console.log)   // 1200
      

      【讨论】:

        【解决方案4】:

        首先,不确定您是否想在您的componentWillMount 中执行此操作,最好将它放在componentDidMount 中,并有一些默认状态,一旦完成这些请求就会更新。其次,您要限制您编写的 setState 的数量,因为它们可能会导致额外的重新渲染,这是使用 async/await 的解决方案:

        async componentDidMount() {
        
          // Make first two requests
          const [firstResponse, secondResponse] = await Promise.all([
            axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p1}`),
            axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p2}`)
          ]);
        
          // Make third request using responses from the first two
          const thirdResponse = await axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + firstResponse.data.results.place_id + '&destination=place_id:' + secondResponse.data.results.place_id + '&key=' + 'API-KEY-HIDDEN');
        
          // Update state once with all 3 responses
          this.setState({
            p1Location: firstResponse.data,
            p2Location: secondResponse.data,
            route: thirdResponse.data,
          });
        
        }
        

        【讨论】:

        • 完美。这很棒。
        • 如果第一个请求出错(仅获取 firstResponse),这种模式如何处理这种情况?
        • @Dravidian 你可以用 try/catch 包装或者在 promise.all 上链接一个 .catch。如果您想处理一个失败而其他通过的情况,您也可以换成使用allSettled 而不是all。在第三个请求中使用之前,您可能还需要更多关于实际存在的 firstResponse.data.results.place_id 值的逻辑。但总体思路是有的
        • 如何访问 Promise.all() 中的状态?
        【解决方案5】:

        为了更好的性能和更简洁的代码

        1、使用promise.all()或者axios.all()同时执行request1和request2。所以 request2 将在不等待 request1 响应的情况下执行。 request1和request2返回响应后,request3会根据返回的响应数据作为参数继续执行。
        2. 模板字符串使用反引号(``)

        async componentDidMount(){
            try{
                const [request1, request2] = await Promise.all([
                   axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p1}`),
                   axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p2}`)
                ]);
        
                const request3 = await axios.get(`https://maps.googleapis.com/maps/api/directions/json?origin=place_id:${request1.data.results.place_id}&destination=place_id:${request2.data.results.place_id}&key=${API-KEY-HIDDEN}`);
                console.log(request3);
            }
            catch(err){
                console.log(err)
            }
        }
        

        【讨论】:

        • 要在axios.all()的情况下单独捕获错误,请使用this
        • 如何捕捉来自不同请求的响应?感谢这个顺便说一句
        【解决方案6】:

        你用过 axios.all 吗?你可以试试类似的东西:

        axios.all([axios.get(`firstrequest`),
                   axios.get(`secondrequest`),
                   axios.get(`thirdrequest`)])
             .then(axios.spread((firstResponse, secondResponse, thirdResponse) => {  
                 console.log(firstResponse.data,secondResponse.data, thirdResponse.data);
             }))
             .catch(error => console.log(error));
        

        这将占用您所有的 get 并将其放入必须使用 .data 调用的响应中,例如: firstResponse.data

        【讨论】:

        • 如果'secondrequest'的输入来自'firstrequest'的响应呢?
        • 好吧,在这种情况下,我会使用这样的东西:``` axios.get('firstrequest)。然后( firstResponse => { axios.get('secondResponse', firstResponse.data.id) } ) ```
        • 如何在标题中传递Auth
        • 记住这是不推荐使用的,axios官方文档建议使用Promise.all
        • @ZaInKhAn axios.get('request', { headers: { 'X-Octopus-ApiKey': process.env.API_KEY, 'Content-Type': 'application/json' } })
        【解决方案7】:

        这与JS的Promises有关。你可以用不同的方式解决它。对我来说最简单的方法是你应该从第一个到第三个嵌套每个请求。这意味着从第一个请求开始,您应该将第二个axios.get(url) 放入第一个请求的.then(),并将第三个请求放入第二个请求的.then()

        对于一般的承诺,您希望在.then() 部分承诺已解决,并且您可以访问response。这样通过嵌套,就可以用一种不太优雅的方式解决异步的问题。

        【讨论】:

        • 正如所说,它不会是一个优雅的。我建议检查其他答案以获得更好的解决方案。
        【解决方案8】:

        我认为你需要这样的东西:

        const firstRequest = axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
              .then(response => this.setState({ p1Location: response.data }))  }
        
        const secondRequest = axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2)
          .then(response => this.setState({ p2Location: response.data }))
        
        const thirdRequest = axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + this.state.p1Location.results.place_id + '&destination=place_id:' + this.state.p2Location.results.place_id + '&key=' + 'API-KEY-HIDDEN')
          .then(response => this.setState({ route: response.data }))
        
        
        Promise.all([firstRequest, secondRequest])
               .then(() => {
                   return thirdRequest
               })
        

        【讨论】:

        • 如果您注释掉 Promise.all 代码,您会注意到三个 API 调用仍在立即进行。这只能是偶然的,因为 Promise.all 没有生效
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-23
        • 2021-06-21
        • 2015-04-22
        • 1970-01-01
        • 1970-01-01
        • 2020-06-27
        相关资源
        最近更新 更多