TransWikia.com

How to call two async functions one after the other?

Stack Overflow Asked by VINEET SHARMA on July 28, 2020

I am a newbie in JavaScript and presently working with async/await in React Native. I have two async functions getAsyncLocation and getParking. getAsyncLocation gets location from device as well from route params (obtained when routed from another screen). getParking uses the state – mapRegion – assigned by getAsyncLocation to find relevant results.

However when I run them one after the other I am getting:-

[Unhandled promise rejection: TypeError: null is not an object (evaluating 'mapRegion.latitude')]

I have following code:-

const [mapRegion, setmapRegion] = useState(null);

handleMapRegionChange = (mapRegion) => setmapRegion({ mapRegion });

  const handleCenterChange = (lat, lng) => setmapRegion({
    latitude: lat,
    longitude: lng,
    latitudeDelta: 0.0922,
    longitudeDelta: 0.0421,
  })

async function getAsyncLocation() {
    if (route.params) { // obtained from some other screen
      handleCenterChange(route.params.lat, route.params.lng);
      console.log("route params for location ", mapRegion.latitude, mapRegion.longitude);
    }
    else {
      const { status, permissions } = await Permissions.askAsync(
        Permissions.LOCATION
      );

      if (status === "granted") {
        sethasLocationPermission(true);
        let location = await Location.getCurrentPositionAsync({});
        setlocationResult(JSON.stringify(location));
        handleCenterChange(location.coords.latitude, location.coords.longitude);
      } else {
        alert("Location permission denied");
      }
    }
  }

  async function getParkings() {
    console.log("get parking runing")
    console.log("get parkig ", mapRegion.latitude, ", ", mapRegion.longitude);
    await axios.get(`http://${MyIp}:8080/booking/findParking`, {
      params: {
        lat: mapRegion.latitude,
        lng: mapRegion.longitude,
        rad: 10
      },
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      }
    })
      .then((response) => {
        let parkingSpace = response.data;
        let parking = []
        for (let i = 0; i < parkingSpace.length; i++) {
          parking.push({
            spaceId: parkingSpace[i]._id,
            ownerId: parkingSpace[i].owner,
            location:
              { latitude: parkingSpace[i].latitude, longitude: parkingSpace[i].longitude }
          })
        }
        console.log(parking)
        setSpace(parking);
      })
      .catch((err) => {
        console.log(err)
      })
  }

  async function getData() {
    await getAsyncLocation();
    await getParkings();
  }

  useEffect(() => {
    getData();
  }, []);

I have tried to search it in various blogs and stackoverflow question but could not get a way around. Would appreciate any hint.

2 Answers

Ciao, in order to execute getAsyncLocation before getParkings you have to use Promise. But there is another problem: even if you run sequentially getAsyncLocation and getParkings, is not guaranteed that in getParkings mapRegion has the value setted by getAsyncLocation (because Hooks are async!). So how can solve your problem? You have several options. One is use 2 useEffect like this:

useEffect(() => {
   getAsyncLocation(); 
}, []);

useEffect(() => {
   getParkings();
}, [mapRegion]);

This approach has 2 problems:

  1. every time you load the component in which useEffects live, first useEffect will be triggered (and maybe you don't want that).
  2. every time mapRegion changes his value, second useEffect will be triggered (and maybe you don't want that).

So, I suggest a Promise approach as I said but passing value that getParkings needs as a result of getAsyncLocation. Your code becomes:

getAsyncLocation

async function getAsyncLocation() {
return new Promise(resolve => {
   let result = {lat: -1, lng: -1};
   if (route.params) { // obtained from some other screen
     handleCenterChange(route.params.lat, route.params.lng);
     console.log("route params for location ", mapRegion.latitude, mapRegion.longitude);
     result.lat = route.params.lat;
     result.lng = route.params.lng;
     resolve(result);
     return;
   }
   else {
     const { status, permissions } = await Permissions.askAsync(
       Permissions.LOCATION
    );

     if (status === "granted") {
       sethasLocationPermission(true);
       let location = await Location.getCurrentPositionAsync({});
       setlocationResult(JSON.stringify(location));
       handleCenterChange(location.coords.latitude, location.coords.longitude);
       result.lat = location.coords.latitude;
       result.lng = location.coords.longitude;
       resolve(result);
       return;
     } else {
       alert("Location permission denied");
     }
   }
   resolve(result);
});
}

getParkings

async function getParkings(myMapRegion) {
 return new Promise(resolve => {
   console.log("get parking runing")
   console.log("get parkig ", myMapRegion.lat, ", ", myMapRegion.lng);
   await axios.get(`http://${MyIp}:8080/booking/findParking`, {
     params: {
       lat: myMapRegion.lat,
       lng: myMapRegion.lng,
       rad: 10
     },
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
     }
   })
     .then((response) => {
       let parkingSpace = response.data;
       let parking = []
       for (let i = 0; i < parkingSpace.length; i++) {
         parking.push({
           spaceId: parkingSpace[i]._id,
           ownerId: parkingSpace[i].owner,
           location:
             { latitude: parkingSpace[i].latitude, longitude: parkingSpace[i].longitude }
         })
       }
       console.log(parking)
       setSpace(parking);
     })
     .catch((err) => {
       console.log(err)
     })
     resolve();
  });
 }

getData

async function getData() {
   let myMapRegion = await getAsyncLocation();
   await getParkings(myMapRegion);
}

Answered by Giovanni Esposito on July 28, 2020

you can use another useEffect hook watching if the mapRegion state has been changed. (you can have as many useEffect hooks as you want in your app)

useEffect(() => {
    getAsyncLocation(); 
  }, []); // [] without any args, this will be called only once. We want to get device location only once

  useEffect(() => {
    getParkings();
  }, [mapRegion]) // since we have passed mapRegion as arg therefore it would be called whenever mapRegion is updated

Answered by Tarukami on July 28, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP