React中如何在等待异步函数返回时暂停for循环?
问题
遍历数组时希望按顺序显示变量dist,但遍历过程中该变量输出顺序混乱。回调函数内的for循环执行顺序正常,但外层第一个for循环输出无序。已尝试将包含两个循环的函数设为async,但问题未解决。相关代码如下:
async function getStations(pBounds){ let gasMarkers = [] var currTankSize = 0 if (CurrentSize === "TFive"){ currTankSize = TankSize*.25 console.log("currTSIZE: " + currTankSize) } else if(CurrentSize === "Fifty"){ currTankSize = TankSize *.5 console.log("currTSIZE: " + currTankSize) } else if(CurrentSize === "SFive"){ currTankSize = TankSize*.75 console.log("currTSIZE: " + currTankSize) } else{ currTankSize = TankSize console.log("currTSIZE: " + currTankSize)} distCoords.push([waypoints[0][0], waypoints[0][1]]) var distTrack = 0 const service = new google.maps.places.PlacesService(map) const searchPromises = []; for (let i = 0; i < waypoints.length; i+=40){ service.nearbySearch({ location:{ lat: waypoints[i][0], lng:waypoints[i][1]}, radius: '20000', type: ['gas_station'] },callback) console.log("i before the second for loop: " + i) async function callback(results, status){ if(status === google.maps.places.PlacesServiceStatus.OK){ for (var j = 0; j < results.length; j++){ if(google.maps.geometry.poly.containsLocation(results[j].geometry.location,pBounds) === true){ //console.log("GEO: " + results[j].geometry.location.lat()) let dist = await distanceCalc(distCoords[distTrack][0], distCoords[distTrack][1], results[j].geometry.location.lat(), results[j].geometry.location.lng() ) dist = dist / Mpg currTankSize = currTankSize - dist var gasLvl = currTankSize/TankSize console.log("i: " + i + " j: " + j+ " Gas: " + gasLvl) //console.log("LEVEL: " + gasLvl) gasLvl = Math.round(gasLvl *10) / 10 //console.log(results[j]['vicinity']) //console.log(results[j]['plus_code']['compound_code']) if (gasLvl <= .5){ distCoords.push([results[j].geometry.location.lat(), results[j].geometry.location.lng()]) distTrack+=1 currTankSize = TankSize gasMarkers.push(new google.maps.Marker({ position: results[j].geometry.location, map, title: results[j]['name'] })) if(!setGas.has(results[j]['vicinity'])){ var price = getGasStationInfo(results[j].geometry.location.lat(), results[j].geometry.location.lng()) } var info = { name: results[j]['name'], vicinity: results[j]['vicinity'], price: await price } if(!setGas.has(results[j]['vicinity'])){ GasInformation.push(info); setGas.add(results[j]['vicinity']) console.log("INLOOP:" + GasInformation); GasSet = Array.from(new Set(GasInformation)) setValueArray(GasSet) } } } } } } } setGasMarkerArray(gasMarkers) //getGasStationInfo() //console.log("ARRAY: " + GasInformation ) //console.log("SET" + ValueArray); //console.log("Value in the array: "+ValueArray.length); }
async function distanceCalc(lat1, long1, lat2, long2){ const pt1 = {lat: lat1, lng: long1} const pt2 = {lat: lat2, lng: long2} let distanceService = new google.maps.DirectionsService() const dist = await Promise.resolve(distanceService.route({ origin: pt1, destination: pt2, travelMode: google.maps.TravelMode.DRIVING})) var miles = dist.routes[0].legs[0].distance.value miles = miles/1609.34 //console.log("MILES: " + miles) return miles }
解决方案
问题根源是service.nearbySearch是异步回调式API,外层for循环会一次性发起所有请求,而各个请求的回调完成顺序不固定,导致外层循环的输出无序。要解决这个问题,需要把异步回调API包装成Promise,然后用await让循环按顺序执行。
步骤1:修复distanceCalc的Promise封装
原代码中用Promise.resolve包装distanceService.route是错误的,因为route方法本身不返回Promise,需要手动封装:
async function distanceCalc(lat1, long1, lat2, long2){ const pt1 = {lat: lat1, lng: long1} const pt2 = {lat: lat2, lng: long2} let distanceService = new google.maps.DirectionsService() // 手动封装route为Promise const dist = await new Promise((resolve, reject) => { distanceService.route({ origin: pt1, destination: pt2, travelMode: google.maps.TravelMode.DRIVING }, (result, status) => { if (status === google.maps.DirectionsStatus.OK) { resolve(result); } else { reject(new Error(`Distance calculation failed: ${status}`)); } }); }); var miles = dist.routes[0].legs[0].distance.value miles = miles/1609.34 return miles }
步骤2:封装nearbySearch为Promise并按顺序执行循环
把nearbySearch包装成返回Promise的函数,在外层for循环中用await等待每个请求处理完成后再进入下一次循环:
async function getStations(pBounds){ let gasMarkers = [] var currTankSize = 0 if (CurrentSize === "TFive"){ currTankSize = TankSize*.25 console.log("currTSIZE: " + currTankSize) } else if(CurrentSize === "Fifty"){ currTankSize = TankSize *.5 console.log("currTSIZE: " + currTankSize) } else if(CurrentSize === "SFive"){ currTankSize = TankSize*.75 console.log("currTSIZE: " + currTankSize) } else{ currTankSize = TankSize console.log("currTSIZE: " + currTankSize)} distCoords.push([waypoints[0][0], waypoints[0][1]]) var distTrack = 0 const service = new google.maps.places.PlacesService(map) // 遍历循环改为顺序执行 for (let i = 0; i < waypoints.length; i+=40){ console.log("i before the second for loop: " + i) // 封装nearbySearch为Promise const results = await new Promise((resolve, reject) => { service.nearbySearch({ location:{ lat: waypoints[i][0], lng:waypoints[i][1]}, radius: '20000', type: ['gas_station'] }, (results, status) => { if(status === google.maps.places.PlacesServiceStatus.OK){ resolve(results); } else { reject(new Error(`Nearby search failed: ${status}`)); } }); }); // 处理搜索结果 for (var j = 0; j < results.length; j++){ if(google.maps.geometry.poly.containsLocation(results[j].geometry.location,pBounds) === true){ let dist = await distanceCalc(distCoords[distTrack][0], distCoords[distTrack][1], results[j].geometry.location.lat(), results[j].geometry.location.lng() ) dist = dist / Mpg currTankSize = currTankSize - dist var gasLvl = currTankSize/TankSize console.log("i: " + i + " j: " + j+ " Gas: " + gasLvl) gasLvl = Math.round(gasLvl *10) / 10 if (gasLvl <= .5){ distCoords.push([results[j].geometry.location.lat(), results[j].geometry.location.lng()]) distTrack+=1 currTankSize = TankSize gasMarkers.push(new google.maps.Marker({ position: results[j].geometry.location, map, title: results[j]['name'] })) let price; if(!setGas.has(results[j]['vicinity'])){ price = await getGasStationInfo(results[j].geometry.location.lat(), results[j].geometry.location.lng()) } var info = { name: results[j]['name'], vicinity: results[j]['vicinity'], price: price } if(!setGas.has(results[j]['vicinity'])){ GasInformation.push(info); setGas.add(results[j]['vicinity']) console.log("INLOOP:" + GasInformation); GasSet = Array.from(new Set(GasInformation)) setValueArray(GasSet) } } } } } setGasMarkerArray(gasMarkers) }
关键说明
- 把回调式API封装为Promise后,用
await可以强制外层for循环等待当前请求的所有处理逻辑完成,再执行下一次循环,保证i的顺序输出。 - 原代码中
var price = getGasStationInfo(...)没有加await,会导致info.price是Promise对象,修改后添加await确保拿到实际价格值。 - 移除了原有的
searchPromises数组,因为现在不需要并行请求,而是顺序执行。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

