点击按钮需两次跳转:Geolocation坐标获取延迟问题求助
解决首次点击按钮无法获取经纬度的问题
嘿,我明白你遇到的问题了——这其实是JavaScript里异步操作的典型坑,新手很容易踩!
问题根源
你调用的navigator.geolocation.getCurrentPosition是个异步函数,什么意思呢?就是当你执行geoFindMe()的时候,它会立刻发起定位请求,但不会等定位结果回来就继续执行HomeTest()里后面的代码。所以第一次点击按钮时:
- 你调用
geoFindMe(),发起定位请求 - 代码直接跳到后面创建
polygon、调用insideTest()的逻辑 - 这时候
latitude和longitude还没被赋值(因为success回调还没执行),自然判断不出结果 - 等定位结果回来,
success回调把经纬度赋值了,但这时候HomeTest()已经执行完了 - 第二次点击时,
latitude和longitude已经有了上一次的赋值,所以能正常判断跳转
另外你的insideTest函数还依赖了全局变量isInsideTest,这也可能带来意外的问题,比如多次调用时变量状态混乱。
两种解决思路
方案1:用Promise + async/await(更现代的写法)
把geoFindMe()改成返回Promise的函数,这样就能用await等待定位结果返回后再执行后续逻辑:
// 重构geoFindMe为返回Promise的函数 function geoFindMe() { return new Promise((resolve, reject) => { if (!navigator.geolocation) { output.innerHTML = "<p>Your browser doesn't support geolocation.</p>"; reject(new Error("浏览器不支持定位功能")); return; } // 成功获取定位时,把坐标传给resolve function success(position) { resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude }); }; // 失败时触发reject function error() { output.innerHTML = "无法获取你的位置"; alert("请更换浏览器尝试"); reject(new Error("获取定位失败")); }; navigator.geolocation.getCurrentPosition(success, error); }); } // 把HomeTest改成async函数,用await等待定位结果 async function HomeTest(){ try { // 等待geoFindMe返回坐标 const { latitude, longitude } = await geoFindMe(); const polygon = [ [ longitude1, latitude1], [ longitude2, latitude2], [ longitude3, latitude3], [ longitude4, latitude4] ]; // 调用修改后的insideTest,直接获取返回结果 const isInside = insideTest([ longitude, latitude ], polygon); if(isInside){ location.href = './html/testhome.html'; } } catch (err) { // 处理定位失败的情况 console.error(err); } } // 修改insideTest,用局部变量代替全局变量,避免状态混乱 function insideTest(point, vs) { let x = point[0], y = point[1]; let isInside = false; for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) { let xi = vs[i][0], yi = vs[i][1]; let xj = vs[j][0], yj = vs[j][1]; let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) isInside = !isInside; } return isInside; }
方案2:用回调函数(更传统的写法)
如果不想用Promise,也可以给geoFindMe()传入一个回调函数,等拿到坐标后再执行判断和跳转:
// 给geoFindMe添加回调参数 function geoFindMe(callback) { if (!navigator.geolocation){ output.innerHTML = "<p>Your browser doesn't support geolocation.</p>"; return; } function success(position) { const latitude = position.coords.latitude; const longitude = position.coords.longitude; // 拿到坐标后执行传入的回调函数 callback(latitude, longitude); }; function error() { output.innerHTML = "The site was not able to locate you"; alert("Please use another browser."); }; navigator.geolocation.getCurrentPosition(success, error); } function HomeTest(){ // 传入回调函数,在拿到坐标后执行判断逻辑 geoFindMe((latitude, longitude) => { const polygon = [ [ longitude1, latitude1], [ longitude2, latitude2], [ longitude3, latitude3], [ longitude4, latitude4] ]; const isInside = insideTest([ longitude, latitude ], polygon); if(isInside){ location.href = './html/testhome.html'; } }); } // 同样修改insideTest用局部变量 function insideTest(point, vs) { let x = point[0], y = point[1]; let isInside = false; for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) { let xi = vs[i][0], yi = vs[i][1]; let xj = vs[j][0], yj = vs[j][1]; let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) isInside = !isInside; } return isInside; }
核心要点
不管用哪种方案,核心都是确保在拿到定位结果(经纬度)之后,再执行判断是否在多边形内和跳转的逻辑,而不是发起请求后立刻执行后续代码。同时尽量避免使用全局变量,改用局部变量或者返回值,减少意外的bug。
内容的提问来源于stack exchange,提问作者Brosi91
相关产品推荐
相关产品推荐

