Axios: chaining multiple API requests

前端 未结 7 741
失恋的感觉
失恋的感觉 2020-12-07 14:15

I need to chain a few API requests from the Google Maps API, and I\'m trying to do it with Axios.

Here is the first request, which is in componentWillMount()

相关标签:
7条回答
  • 2020-12-07 14:26

    For better performance and cleaner code:

    1. Use promise.all() or axios.all() to execute request1 and request2 at the same time. So request2 will execute without waiting for request1 response. After request1 and request2 return the response, request3 will continue execute based on the returned response data as parameter.
    2. Template Strings use back-ticks (``)

    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)
        }
    }
    
    0 讨论(0)
  • 2020-12-07 14:28

    First off, not sure you want to do this in your componentWillMount, it's better to have it in componentDidMount and have some default states that will update once done with these requests. Second, you want to limit the number of setStates you write because they might cause additional re-renders, here is a solution using 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,
      });
    
    }
    
    0 讨论(0)
  • 2020-12-07 14:30

    This is related to JS's Promises. You can solve it in different ways. The simplest way to me is that you should nest each request starting from first to third. That means starting from the first request, you should put your second axios.get(url) into the first request's .then() and put the third request into the second request's .then().

    For promises in general you expect that inside the .then() part promise is resolved and you can have access to the response. So that by nesting, you can solve the problem of being asynchronous in a not so elegant way.

    0 讨论(0)
  • 2020-12-07 14:38

    create array of promise and then use 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
    
    0 讨论(0)
  • 2020-12-07 14:42

    Have you used axios.all ? You can try with something similar:

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

    This will take all your get and will put it inside a response that has to be called with .data like: firstResponse.data

    0 讨论(0)
  • 2020-12-07 14:45

    I think you need something like this:

    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
           })
    
    0 讨论(0)
提交回复
热议问题