4

Before I start, let me say that I'm new to Javascript and very new to axios API calls, so I'm probably making a rookie mistake...

I have this function getObjects() that's meant to map over an array and return the data from an Axios API call. The API call and map function are both working, but I'm getting a Promise object instead of the data I'm trying to get.

I figure this is because the data is returned before there's enough time to actually get it, but not sure how to fix? I tried a .setTimeout(), but that didn't seem to work.

  getObjects() {
    let newsItems = this.state.arrayofids.map((index) => {
      let resultOfIndex = axios.get(`https:\/\/hacker-news.firebaseio.com/v0/item/${index}.json`).then((res) => {
          let data = res.data;
          //console.log(data.by); // this prints the correct byline, but
                              // all the bylines are printed after
                              // the console.log below...
          if (!data.hasOwnProperty('text')) return data;
        }); /// end of axios request
  return resultOfIndex;
    }); /// end of map
    /// ideally this would end in an array of response objects but instead i'm getting an array of promises...
    console.log(newsItems);
  }

(The extra escape characters are for my text editor's benefit.)

Here's a link to a codepen with the issue - open up the console to see the problem. It's a React project but I don't think any of the React stuff is the issue. EDIT: Codepen is link to working solution using axios.all as suggested below

Thanks!

EDIT: Here is my working solution.

getObjects() {
  let axiosArr = [];
  axios.all(this.state.arrayofids.map((id) => {
      return axios.get(`https:\/\/hacker-news.firebaseio.com/v0/item/${id}.json`)
    })).then((res) => {
      for (let i = 0; i < this.state.arrayofids.length; i++) {
        axiosArr.push(<li key={i} data-url={res[i].data.url} onClick={(e) => this.displayTheNews(e)}>{res[i].data.title}</li>);
      }
      if (axiosArr.length == this.state.arrayofids.length) {
        this.setState({arrayofdata: axiosArr});
        console.log('state is set!');
      }
    })
 }
JSilv
  • 1,035
  • 12
  • 26
  • 1
    Not an expert, but I've used it for a little while. I'm pretty sure that it only returns promises as there's a `axios.all` function. You will have to work out the logic inside the then, which I suggest using `this.setState` or redux equivalent. You actually might want to look at `axios.all` instead of doing this mapping. – A. L Jan 07 '17 at 16:52
  • @A.Lau Oh, great, this definitely put me on the right path. Thanks so much. – JSilv Jan 07 '17 at 17:06
  • I'll put it as answer then – A. L Jan 07 '17 at 17:10

2 Answers2

2

axios.all function should be more appropriate to your current scenario.

A. L
  • 11,695
  • 23
  • 85
  • 163
1

Your console.log is executing immediately, rather than waiting for the requests to finish, because they are not synchronous. You have to wait for all the responses before you console.log.

OPTION 1 (the hard way): replace your console.log with

newsItems.forEach((promise, index) => {
  promise.then((object)=>{
    newsItems[index] = object
    if (index+1 == newsItems.length) {
      console.log(newsItems)
    }
  })
})


OPTION 2 (the better way): using axios.all

  getObjects() {
    axios.all(this.state.arrayofids.map((id) => {
      return axios.get(`https:\/\/hacker-news.firebaseio.com/v0/item/${id}.json`)
    })).then((res) => {
      console.log(res)
    })
  }

by the way, I would definitely reccommend changing

this.state.arrayofids.map((index) => {
      let resultOfIndex = axios.get(`https:\/\/hacker-news.firebaseio.com/v0/item/${index}.json`)...

to be called id instead of index

Ethan Rose
  • 312
  • 2
  • 10