Promise.then is not a function - handling multiple API calls in React

29,573

Solution 1

Your problem is that getAsync does not always return a promise, so you could not chain .then(…) to every call. When there is no input, you were returning a plain object - instead you need to return a promise that is resolved with that object:

if (!input) {
   return Promise.resolve({ options: [] });
}

Solution 2

So it turns out the if statement was causing the error:

if (!input) {
  return {options: []}
}

but I have no idea why that would. If someone could explain why, that would be good to know for future issues.

Here's the solution I got following @Bergi's advice avoiding the Promise Constructor antipattern

const loadOptions = (tripId, destinationIndex, input) => {

  function getMusement(input) {
    return TVApi.musement.autocomplete(input)
      .then((m) => {
        const musementOptions = m.map(musementToOption).slice(0, 3)
        return musementOptions
      })
  }

  function getFourSquare(tripId, destinationIndex, input) {
    return TVApi.spot.autocomplete(tripId, destinationIndex, input)
      .then((fs) => {
        const fsOptions = fs.map(fsToOption).slice(0, 2)
        return fsOptions
      })
  }

  return Promise.all([getMusement(input), getFourSquare(tripId, destinationIndex, input)])
    .then((allData) => {
      const merged = [].concat.apply([], allData)
      return {options: merged}
    })
}
Share:
29,573
crash springfield
Author by

crash springfield

Updated on May 12, 2020

Comments

  • crash springfield
    crash springfield almost 4 years

    I'm using react-select to auto-complete options in a search bar. The search bar displays the results in one of two categories, depending on which API endpoint it hits.

    Right now, it works with data from either one point or the other, but I'm having trouble returning data from both endpoints to react-select's loadOptions parameter.

    From this answer about multiple API calls, I decided to use promises to return all the data at once, but I get the error Uncaught TypeError: promise.then is not a function at Async.loadOptions

    Here's my code for loadOptions:

    const getAsync = (tripId, destinationIndex, input) => {
      if (!input) {
        return { options: [] }
      }
    
      function getMusement(input) {
        return new Promise(function(resolve, reject) {
          TVApi.musement.autocomplete(input)
            .then((m) => {
              const musementOptions = m.map(musementToOption).slice(0, 4)
              return resolve(musementOptions)
            })
        })
      }
    
      function getFourSquare(tripId, destinationIndex, input) {
        return new Promise(function(resolve, reject) {
          TVApi.spot.autocomplete(tripId, destinationIndex, input)
            .then((fs) => {
              const fsOptions = fs.map(spotToOption).slice(0, 4)
              return resolve(fsOptions)
            })
        })
      }
    
      return Promise.all([getMusement(input), getFourSquare(tripId, destinationIndex, input)])
        .then((allData) => {
          const merged = [].concat.apply([], allData)
          console.log(JSON.stringify(merged)) // logs out with correct data
          return {options: merged}
        })
    }
    
  • Bergi
    Bergi almost 7 years
    Thanks for the hint, see my answer for why that is the problem and how to solve it
  • crash springfield
    crash springfield almost 7 years
    For some reason this doesn't work, but it might have to do with the react-select library. The if statement now just makes the search bar give up. Back when it was making just one API call (using a callback) the search bar would always respond to new inputs. Now it doesn't respond.
  • Bergi
    Bergi almost 7 years
    I thought previously it would throw an error? Now at least it doesn't do that :-) Not sure how you use it, and why resolving the promise will prevent it from searching further, but you should ask a separate question with the select code about this issue.