简体   繁体   中英

How to call two async functions one after the other?

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.

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

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 useEffect s 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM