开发者问题收集

如何依次调用两个异步函数?

2020-07-25
1718

我是 JavaScript 新手,目前正在使用 React Native 中的 async/await。我有两个异步函数 getAsyncLocationgetParkinggetAsyncLocation 从设备以及路由参数(从另一个屏幕路由时获得)获取位置。 getParking 使用由 getAsyncLocation 分配的状态 - mapRegion - 来查找相关结果。

但是,当我一个接一个地运行它们时,我得到了:-

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

我有以下代码:-

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();
  }, []);

我曾尝试在各种博客和 stackoverflow 问题中搜索它,但找不到解决方法。非常感谢任何提示。

2个回答

您可以使用另一个 useEffect 钩子来观察 mapRegion 状态是否已更改。(您可以在应用中拥有任意数量的 useEffect 钩子)

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
Tarukami
2020-07-25

你好,为了在 getParkings 之前执行 getAsyncLocation ,您必须使用 Promise。但还有另一个问题:即使您按顺序运行 getAsyncLocationgetParkings ,也不能保证 getParkings 中的 mapRegion 具有由 getAsyncLocation 设置的值(因为 Hooks 是异步的!)。那么如何解决您的问题?您有几种选择。一种是像这样使用 2 个 useEffect:

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

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

这种方法有两个问题:

  1. 每次加载包含 useEffect 的组件时,都会触发第一个 useEffect(也许您不希望这样)。
  2. 每次 mapRegion 更改其值时,都会触发第二个 useEffect (也许您不希望这样)。

因此,我建议使用 Promise 方法,正如我所说,但传递 getParkings 所需的值作为 getAsyncLocation 的结果。您的代码变为:

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);
}
Giovanni Esposito
2020-07-26