loading...

Fetching With Async/Await

loripb profile image Lori "Lei" Boyd ・2 min read

I was going through a refactoring phase in my last project and decided to tidy up my fetch requests. I have multiple functions that not only look similar, but they all are about 49 lines long.

handleClick = () => {
    // fetches arrivalTimes
    fetch(`https://subway-times-api.herokuapp.com/stops/${this.props.stop.id}`)
    .then(r => r.json())
    .then(data => {
      // fetch here
      fetch(`https://subway-times-api.herokuapp.com/lines/${data.lines[0].id}`)
      .then(r => r.json())
      .then((line) => {
        // diggs through feed to find the arrays with the arrival times
        let feed = line.feed.filter( obj => Object.keys(obj).includes("trip_update"))
        let includesStopTimeUpdate = feed.filter(obj => Object.keys(obj.trip_update).includes("stop_time_update"))
        let stopTimeUpdateArrays = includesStopTimeUpdate.map(obj => obj.trip_update.stop_time_update)
        let stopTimeArrays = stopTimeUpdateArrays.map(obj => obj.map(obj2 => obj2))

        let trainObjs = []

        // adds the objects with train arrival times and stop ids to "state"
        stopTimeArrays.map(obj => obj.map(obj2 => trainObjs.push(obj2)))

        let arrivalTimes = trainObjs.filter(obj => obj.stop_id.includes(this.props.stop.stop_id + this.props.direction.direction))
        let trainArrivalObjs = arrivalTimes.map(obj => {
          let trainTime = new Date( parseInt(obj.arrival.time) *1000);
          let timeNow = new Date()

          // setting hours and mins
          let trainHour = trainTime.getHours() > 12? trainTime.getHours() - 12 : trainTime.getHours()
          let trainMin = trainTime.getMinutes()
          let currentHour = timeNow.getHours() > 12? timeNow.getHours() - 12 : timeNow.getHours()
          let currentMin = timeNow.getMinutes()

          // if trainHour is > current hour add 60 mins to trainMin
          if (trainHour > currentHour) {
            trainMin += 60
          }

          // take hour and min of train time and subtract each from the current time, if result is negative return 0
          return trainMin - currentMin
        })

        // if train is due or has past remove
        const arrivals = trainArrivalObjs.filter(time => time >= 0)

        this.setState({
          renderStopInfo: !this.state.renderStopInfo,
          arrivals: arrivals
        })
      })
    })
  }

This function throws away the whole DRY rule, and it's time to fix that. I've decided to throw all the logic here into my backend, so when I fetch for a stop, I get back all uptown and downtown arrival times converted to hours and minutes. To further refactor, I've decided to use async and await to handle my fetch.

Async

Async is a keyword before a function, and when called, the function returns a promise.

async function hello() {
    return "Hello, World!"
}
console.log(hello()) => Promise {<resolved>: "Hello, World!"}

Await

The MDN docs state "This [await function] can be put in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value. In the meantime, other code that may be waiting for a chance to execute gets to do so."

So instead of writing my event handlers like so:

fetch(API)
.then(response => responce.json())
.then(data => this.setState({apiObject: data})

I can write the same thing with async/await

async function fetchFromApi(api) {
  let response = await fetch(api)
  let apiObject = response.json()
  return apiobject
}

fetchFromApi(API).then((apiObject)) => { 
  this.setState({
    apiObject: responseObject 
  })
})

In my opinion, the later is a lot easier to read. Also, because It's reusable, I can just call on fetchFromApi with any URL.

Posted on May 30 by:

Discussion

markdown guide
 

Great post Lori!
I've found that for doing routine fetch calls like this, its even easier to do

const data = await (await fetch(url)).json();

This way you don't add unnecessary variables or add to the call stack, but still keep your fetch call down to one line.

 

Nice! Thanks for the tip, Taylor!