为什么搭配await使用的地理定位回调函数始终未被调用?
问题根源
navigator.geolocation.getCurrentPosition是经典的回调风格异步API,本身的返回值是undefined,完全不是Promise对象。
根据await的语法规则:如果await后面跟的不是Promise,会直接把这个值包装成一个已经完成的Promise,完全不会等待后续的异步流程执行。这就是为什么程序根本没等你点击授权弹窗就直接往下跑了。
原代码的实际执行流程
你遇到的所有异常表现都可以用这个执行逻辑解释:
- 进入
retrieveLocation函数,首先同步打印初始的默认位置信息、输出"Getting location" - 调用
getCurrentPosition时,浏览器会立刻弹出位置授权请求弹窗,但该方法没有任何返回值,await拿到undefined后直接放行后续代码 - 程序同步执行完后续逻辑:打印"Got location"、输出仍然是初始值的
userLocation,整个函数的主体流程直接结束 - 等你在弹窗上完成操作(点允许/阻止)时,对应的成功/失败回调才会延迟触发:
- 如果你点阻止,隔一会才会输出"Something went wrong"
- 如果你点允许,隔一会才会输出"In setUserLocation"——因为这部分日志是在主流程结束很久之后才打出来的,很容易被忽略,给你造成了“回调被永久跳过”的错觉
修正方案
要让地理定位API配合async/await使用,必须先把回调风格的API手动封装成Promise,让await能真正等待异步操作完成。
修正后的可运行代码如下:
var userLocation = {lat: "40.0", lon: "-90.0", name: "Default Location"} // 将原生回调式的geolocation API封装为Promise方法 function getCurrentPosition(options = {}) { return new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject, options) }) } // 位置获取成功后的处理逻辑 function setUserLocation(position) { console.log("In setUserLocation") const lat = position.coords.latitude.toString() const lon = position.coords.longitude.toString() userLocation.lat = lat userLocation.lon = lon userLocation.name = "New Location" console.log(userLocation) } async function retrieveLocation() { console.log(userLocation) console.log("Getting location") try { // await等待Promise完成,会一直暂停到授权操作完成、位置信息获取成功才往下执行 const position = await getCurrentPosition() setUserLocation(position) } catch (err) { // 授权被拒、位置获取失败的逻辑会在这里被捕获 console.log("Something went wrong", err) } console.log("Got location") console.log(userLocation) } retrieveLocation()
其他细节提醒
- 原代码里
setUserLocation函数内的lat、lon没有加变量声明,会意外挂载到全局window对象上,属于容易引发变量污染的不规范写法,修正时已经补上了const声明 - 原来的代码因为没有将API封装为Promise,内置的错误捕获逻辑根本无法接收到定位过程抛出的错误,哪怕出现授权拒绝、定位超时等问题,也无法通过async/await的错误处理机制捕获
内容的提问来源于stack exchange,提问作者Grant Petty
相关产品推荐
相关产品推荐

