[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-getcurrentposition-not-a-promise":44,"search-suggestions":270,"path-membership-getcurrentposition-not-a-promise":311,"related-getcurrentposition-not-a-promise":312,"code:js:true:142cusq":439,"code:js:true:1qhb4vc":440,"code:js:true:temxf2":441,"code:js:true:hjugyr":442,"code:js:true:j0g9bu":443,"code:js:true:l3z97a":444,"code:js:true:r61v1c":445,"comments-01a18647-e99b-71d4-a0db-6ad3f6778daf":446},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":253,"seo":254,"translationGroupId":257,"series":48,"podcastUrl":48,"verticalId":5,"thread":258,"assessments":260,"translations":266,"quiz":268},"01a18647-e99b-71d4-a0db-6ad3f6778daf","getcurrentposition-not-a-promise","getCurrentPosition() Has No Default Timeout. That's the Trap.",null,"A \"find my location\" button that works instantly on your laptop and hangs forever on a phone with a weak signal isn't flaky — it's getCurrentPosition() doing exactly what its defaults tell it to do. No timeout, no cached position, and a watcher nobody told to stop.","\u002Fmedia\u002Fcovers\u002Fgetcurrentposition-not-a-promise.png",6,"2026-11-04T06:10:25.023Z",25,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"programming","Programming",{"slug":73,"name":74,"color":48},"tutorial","Tutorial",{"assessments":76},1,{"slug":46,"title":78},"getCurrentPosition() — interactive playground",{"blocks":80,"version":76},[81,85,88,91,95,101,104,107,110,114,117,121,124,128,131,134,138,142,145,149,152,156,159,163,166,170,174,177,180,183,186,189,192,195,198,201,204,207,210,213,216,219,222,225,228,235,238,241,244,247],{"id":82,"html":83,"type":84},"b1","\u003Cp>The ticket read: &quot;location button sometimes just spins forever.&quot; Not a crash, not an error toast — just a spinner that never stopped. QA couldn&#39;t reproduce it. It worked every time on the office Wi-Fi, every time on a laptop, every time in the demo to the client.\u003C\u002Fp>","paragraph",{"id":86,"html":87,"type":84},"b2","\u003Cp>It only happened out in the field. On a phone. Somewhere the GPS fix took its time and the Wi-Fi-based positioning fallback was slow to answer. Which meant it was exactly the one environment nobody tested in before shipping.\u003C\u002Fp>",{"id":89,"html":90,"type":84},"b3","\u003Cp>Here&#39;s the part that made it worse: the code had an error handler. Both arguments to \u003Ccode>getCurrentPosition()\u003C\u002Fcode> were filled in, success and failure. By every code-review instinct, this was handled.\u003C\u002Fp>",{"id":92,"html":93,"text":93,"type":94,"level":31},"b4","The code that looks fully handled","heading",{"id":96,"code":97,"type":98,"language":99,"highlight":100},"b5","function useMyLocation(onFound, onFail) {\n  navigator.geolocation.getCurrentPosition(\n    (pos) => onFound(pos.coords),\n    (err) => onFail(err.message)\n  );\n}","code","js",[],{"id":102,"html":103,"type":84},"b6","\u003Cp>A success callback, an error callback, nothing swallowed. If you squint, it even looks defensive — most \u003Ccode>getCurrentPosition()\u003C\u002Fcode> snippets you&#39;ll find in the wild look exactly like this.\u003C\u002Fp>",{"id":105,"html":106,"type":84},"b7","\u003Cp>\u003Cstrong>Before you keep reading: what has to happen for that error callback to fire at all?\u003C\u002Fstrong> Not &quot;what does it do&quot; — when does the browser decide to call it?\u003C\u002Fp>",{"id":108,"html":109,"type":84},"b8","\u003Cp>The honest answer is: only when the browser gives up and reports a \u003Ccode>GeolocationPositionError\u003C\u002Fcode> — permission denied, position unavailable, or a timeout. And a timeout is an \u003Cem>option you pass in\u003C\u002Fem>. If you don&#39;t pass one, there&#39;s nothing to time out. The browser just... keeps trying. Forever, if it has to. Neither callback is under any obligation to ever run.\u003C\u002Fp>",{"id":111,"html":112,"text":113,"type":94,"level":31},"b9","The default timeout is not &quot;some reasonable number.&quot; It&#39;s \u003Ccode>Infinity\u003C\u002Fcode>.","The default timeout is not \"some reasonable number.\" It's Infinity.",{"id":115,"html":116,"type":84},"b10","\u003Cp>\u003Ccode>getCurrentPosition()\u003C\u002Fcode> takes a third, optional \u003Ccode>options\u003C\u002Fcode> argument: \u003Ccode>{ enableHighAccuracy, timeout, maximumAge }\u003C\u002Fcode>. Skip it — like the snippet above does — and you get the spec&#39;s own defaults:\u003C\u002Fp>",{"id":118,"code":119,"type":98,"language":99,"highlight":120},"b11","{ enableHighAccuracy: false, timeout: Infinity, maximumAge: 0 }",[],{"id":122,"html":123,"type":84},"b12","\u003Cp>\u003Ccode>timeout: Infinity\u003C\u002Fcode> means precisely what it says: the call will wait as long as the device needs to produce a position, with no upper bound. On a laptop with recent Wi-Fi positioning data cached nearby, that&#39;s near-instant, so the bug never shows up in testing. On a phone with a weak signal indoors, &quot;as long as it needs&quot; can be minutes — or, if the underlying location provider never calls back at all, forever. There&#39;s no silent fallback, no eventual error. The spinner you built to show \u003Cem>while waiting\u003C\u002Fem> just keeps being accurate.\u003C\u002Fp>",{"id":125,"html":126,"text":127,"type":94,"level":31},"b13","The other default nobody reads: \u003Ccode>maximumAge: 0\u003C\u002Fcode>","The other default nobody reads: maximumAge: 0",{"id":129,"html":130,"type":84},"b14","\u003Cp>\u003Ccode>maximumAge\u003C\u002Fcode> controls whether the browser is allowed to hand back a cached position instead of asking the device for a brand-new fix. The default is \u003Ccode>0\u003C\u002Fcode> milliseconds — meaning no cache is old enough to use, ever. Every single call to \u003Ccode>getCurrentPosition()\u003C\u002Fcode>, even one fired a hundred milliseconds after the last one, forces a fresh GPS\u002FWi-Fi\u002Fcell fix from scratch.\u003C\u002Fp>",{"id":132,"html":133,"type":84},"b15","\u003Cp>That&#39;s rarely what you actually want. A &quot;locate me&quot; button a user double-clicks, or a component that calls \u003Ccode>getCurrentPosition()\u003C\u002Fcode> on mount right after a sibling component just did the same thing, don&#39;t need two independent fixes a tenth of a second apart. Passing a real \u003Ccode>maximumAge\u003C\u002Fcode> — &quot;a position up to 30 seconds old is fine&quot; — turns repeat calls into near-free cache hits instead of two full location requests:\u003C\u002Fp>",{"id":135,"code":136,"type":98,"language":99,"highlight":137},"b16","navigator.geolocation.getCurrentPosition(onFound, onFail, {\n  timeout: 10_000,\n  maximumAge: 30_000,\n});",[],{"id":139,"html":140,"text":141,"type":94,"level":31},"b17","\u003Ccode>watchPosition()\u003C\u002Fcode> has the same shape of bug as a forgotten \u003Ccode>clearInterval\u003C\u002Fcode>","watchPosition() has the same shape of bug as a forgotten clearInterval",{"id":143,"html":144,"type":84},"b18","\u003Cp>\u003Ccode>getCurrentPosition()\u003C\u002Fcode> asks once. \u003Ccode>watchPosition()\u003C\u002Fcode> asks continuously, calling your success callback every time the device&#39;s position changes meaningfully — useful for live tracking, useless the moment nobody&#39;s watching the screen anymore. It returns a watch ID, and that ID is your responsibility:\u003C\u002Fp>",{"id":146,"code":147,"type":98,"language":99,"highlight":148},"b19","const watchId = navigator.geolocation.watchPosition(onMove, onError, {\n  enableHighAccuracy: true,\n});\n\n\u002F\u002F later, when you're done:\nnavigator.geolocation.clearWatch(watchId);",[],{"id":150,"html":151,"type":84},"b20","\u003Cp>Skip that last line — the same way it&#39;s easy to skip \u003Ccode>clearInterval()\u003C\u002Fcode> — and the watch keeps running after the component that started it is gone. In a React component, that&#39;s a \u003Ccode>watchPosition()\u003C\u002Fcode> call in a \u003Ccode>useEffect\u003C\u002Fcode> with no cleanup function: the GPS radio stays active, the callback keeps firing into a component that no longer exists, and the battery drain shows up in a support ticket that has nothing to do with location on its face.\u003C\u002Fp>",{"id":153,"code":154,"type":98,"language":99,"highlight":155},"b21","useEffect(() => {\n  const id = navigator.geolocation.watchPosition(onMove, onError);\n  return () => navigator.geolocation.clearWatch(id);\n}, []);",[],{"id":157,"html":158,"type":84},"b22","\u003Cp>One line in the cleanup function. Easy to write, easy to forget, and nothing in the API warns you that you forgot it.\u003C\u002Fp>",{"id":160,"html":161,"text":162,"type":94,"level":31},"b23","It&#39;s not a Promise, and that&#39;s the last gotcha","It's not a Promise, and that's the last gotcha",{"id":164,"html":165,"type":84},"b24","\u003Cp>\u003Ccode>getCurrentPosition()\u003C\u002Fcode> predates \u003Ccode>fetch()\u003C\u002Fcode>, predates \u003Ccode>Promise\u003C\u002Fcode>, and was never updated to support either — it&#39;s success\u002Ferror callbacks, full stop. You can&#39;t \u003Ccode>await\u003C\u002Fcode> it directly. The fix is a wrapper you write once and reuse everywhere:\u003C\u002Fp>",{"id":167,"code":168,"type":98,"language":99,"highlight":169},"b25","function getPosition(options) {\n  return new Promise((resolve, reject) => {\n    navigator.geolocation.getCurrentPosition(resolve, reject, {\n      enableHighAccuracy: false,\n      timeout: 10_000,\n      maximumAge: 30_000,\n      ...options,\n    });\n  });\n}",[],{"id":171,"code":172,"type":98,"language":99,"highlight":173},"b26","try {\n  const pos = await getPosition();\n  useLocation(pos.coords);\n} catch (err) {\n  if (err.code === err.PERMISSION_DENIED) showPermissionHelp();\n  else if (err.code === err.TIMEOUT) showRetry();\n  else showGenericError();\n}",[],{"id":175,"html":176,"type":84},"b27","\u003Cp>Every default that bit the original snippet is now an explicit choice, and the whole thing reads top to bottom instead of nesting two callbacks inside each other.\u003C\u002Fp>",{"id":178,"html":179,"text":179,"type":94,"level":31},"b28","The one real constraint: it needs a secure origin",{"id":181,"html":182,"type":84},"b29","\u003Cp>This part isn&#39;t a trap, just worth stating plainly: every major browser requires a secure context — HTTPS, or \u003Ccode>localhost\u003C\u002Fcode> in development — before it will even ask for location permission. That&#39;s been true for close to a decade now, not a new restriction, and there&#39;s no workaround or flag to reach for; if a \u003Ccode>getCurrentPosition()\u003C\u002Fcode> call is silently failing in a way none of the above explains, check the protocol in the address bar first.\u003C\u002Fp>",{"id":184,"html":185,"type":84},"b30","\u003C!-- playground:start -->",{"id":187,"html":188,"text":188,"type":94,"level":31},"b31","🎮 Try it yourself",{"id":190,"html":191,"type":84},"b32","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fgetcurrentposition-not-a-promise\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":193,"html":194,"type":84},"b33","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":196,"html":197,"type":84},"b34","\u003C!-- playground:end -->",{"id":199,"html":200,"type":84},"b35","\u003Cp>The playground above simulates the GPS fix with a slider, so you get the exact same race every time instead of waiting on whatever signal your actual device has right now. Set a slow &quot;fix,&quot; turn on &quot;use the default options,&quot; and watch the stopwatch climb with nothing to stop it — that&#39;s \u003Ccode>timeout: Infinity\u003C\u002Fcode>, reproduced on demand. Then try the \u003Ccode>watchPosition()\u003C\u002Fcode> panel: start it, hit &quot;simulate component unmount&quot; without stopping it first, and watch the counter keep climbing anyway.\u003C\u002Fp>",{"id":202,"html":203,"type":84},"b36","\u003C!-- quiz:start -->",{"id":205,"html":206,"text":206,"type":94,"level":31},"b37","🧠 Test yourself",{"id":208,"html":209,"type":84},"b38","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fgetcurrentposition-not-a-promise\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":211,"html":212,"type":84},"b39","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":214,"html":215,"type":84},"b40","\u003C!-- quiz:end -->",{"id":217,"html":218,"type":84},"b41","\u003Cp>The fix for that &quot;spins forever&quot; ticket was three lines: a \u003Ccode>timeout\u003C\u002Fcode>, a \u003Ccode>maximumAge\u003C\u002Fcode>, and a \u003Ccode>clearWatch()\u003C\u002Fcode> in a cleanup function that had never been written. None of it was hard once someone knew to look for it — the hard part was realizing that &quot;sometimes it just hangs&quot; wasn&#39;t a flaky bug report. It was the spec&#39;s own default, read literally.\u003C\u002Fp>",{"id":220,"html":221,"type":84},"b42","\u003Cp>Does your codebase have a bare \u003Ccode>getCurrentPosition()\u003C\u002Fcode> call anywhere without a \u003Ccode>timeout\u003C\u002Fcode>? Worth a five-minute grep before it becomes someone else&#39;s &quot;impossible to reproduce&quot; ticket.\u003C\u002Fp>",{"id":223,"html":224,"type":84},"b43","\u003C!-- related:start -->",{"id":226,"html":227,"text":227,"type":94,"level":31},"b44","📚 Read next",{"id":229,"type":230,"items":231,"ordered":18},"b45","list",[232,233,234],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Funhandledrejection-swallowed-promise-errors\">Stop Losing Promise Errors. \u003Ccode>unhandledrejection\u003C\u002Fcode> Catches Them.\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fpromise-try-synchronous-throw-safety\">My .catch() Never Ran. The Bug Was Two Lines Above It\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-proxy-reflect\">JavaScript Proxy and Reflect: The Complete Guide\u003C\u002Fa>",{"id":236,"html":237,"type":84},"b46","\u003C!-- related:end -->",{"id":239,"type":240},"b47","divider",{"id":242,"html":243,"type":84},"b48","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":245,"html":246,"type":84},"b49","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":248,"type":230,"items":249,"ordered":18},"b50",[250,251,252],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","The ticket read: \"location button sometimes just spins forever.\" Not a crash, not an error toast — just a spinner that never stopped. QA couldn't reproduce it. It worked every time on the office Wi-Fi, every time on a laptop, every time in the demo to the client.\n\nIt only happened out in the field. On a phone. Somewhere the GPS fix took its time and the Wi-Fi-based positioning fallback was slow to answer. Which meant it was exactly the one environment nobody tested in before shipping.\n\nHere's the part that made it worse: the code had an error handler. Both arguments to `getCurrentPosition()` were filled in, success and failure. By every code-review instinct, this was handled.\n\n## The code that looks fully handled\n\n```js\nfunction useMyLocation(onFound, onFail) {\n  navigator.geolocation.getCurrentPosition(\n    (pos) => onFound(pos.coords),\n    (err) => onFail(err.message)\n  );\n}\n```\n\nA success callback, an error callback, nothing swallowed. If you squint, it even looks defensive — most `getCurrentPosition()` snippets you'll find in the wild look exactly like this.\n\n**Before you keep reading: what has to happen for that error callback to fire at all?** Not \"what does it do\" — when does the browser decide to call it?\n\nThe honest answer is: only when the browser gives up and reports a `GeolocationPositionError` — permission denied, position unavailable, or a timeout. And a timeout is an *option you pass in*. If you don't pass one, there's nothing to time out. The browser just... keeps trying. Forever, if it has to. Neither callback is under any obligation to ever run.\n\n## The default timeout is not \"some reasonable number.\" It's `Infinity`.\n\n`getCurrentPosition()` takes a third, optional `options` argument: `{ enableHighAccuracy, timeout, maximumAge }`. Skip it — like the snippet above does — and you get the spec's own defaults:\n\n```js\n{ enableHighAccuracy: false, timeout: Infinity, maximumAge: 0 }\n```\n\n`timeout: Infinity` means precisely what it says: the call will wait as long as the device needs to produce a position, with no upper bound. On a laptop with recent Wi-Fi positioning data cached nearby, that's near-instant, so the bug never shows up in testing. On a phone with a weak signal indoors, \"as long as it needs\" can be minutes — or, if the underlying location provider never calls back at all, forever. There's no silent fallback, no eventual error. The spinner you built to show *while waiting* just keeps being accurate.\n\n## The other default nobody reads: `maximumAge: 0`\n\n`maximumAge` controls whether the browser is allowed to hand back a cached position instead of asking the device for a brand-new fix. The default is `0` milliseconds — meaning no cache is old enough to use, ever. Every single call to `getCurrentPosition()`, even one fired a hundred milliseconds after the last one, forces a fresh GPS\u002FWi-Fi\u002Fcell fix from scratch.\n\nThat's rarely what you actually want. A \"locate me\" button a user double-clicks, or a component that calls `getCurrentPosition()` on mount right after a sibling component just did the same thing, don't need two independent fixes a tenth of a second apart. Passing a real `maximumAge` — \"a position up to 30 seconds old is fine\" — turns repeat calls into near-free cache hits instead of two full location requests:\n\n```js\nnavigator.geolocation.getCurrentPosition(onFound, onFail, {\n  timeout: 10_000,\n  maximumAge: 30_000,\n});\n```\n\n## `watchPosition()` has the same shape of bug as a forgotten `clearInterval`\n\n`getCurrentPosition()` asks once. `watchPosition()` asks continuously, calling your success callback every time the device's position changes meaningfully — useful for live tracking, useless the moment nobody's watching the screen anymore. It returns a watch ID, and that ID is your responsibility:\n\n```js\nconst watchId = navigator.geolocation.watchPosition(onMove, onError, {\n  enableHighAccuracy: true,\n});\n\n\u002F\u002F later, when you're done:\nnavigator.geolocation.clearWatch(watchId);\n```\n\nSkip that last line — the same way it's easy to skip `clearInterval()` — and the watch keeps running after the component that started it is gone. In a React component, that's a `watchPosition()` call in a `useEffect` with no cleanup function: the GPS radio stays active, the callback keeps firing into a component that no longer exists, and the battery drain shows up in a support ticket that has nothing to do with location on its face.\n\n```js\nuseEffect(() => {\n  const id = navigator.geolocation.watchPosition(onMove, onError);\n  return () => navigator.geolocation.clearWatch(id);\n}, []);\n```\n\nOne line in the cleanup function. Easy to write, easy to forget, and nothing in the API warns you that you forgot it.\n\n## It's not a Promise, and that's the last gotcha\n\n`getCurrentPosition()` predates `fetch()`, predates `Promise`, and was never updated to support either — it's success\u002Ferror callbacks, full stop. You can't `await` it directly. The fix is a wrapper you write once and reuse everywhere:\n\n```js\nfunction getPosition(options) {\n  return new Promise((resolve, reject) => {\n    navigator.geolocation.getCurrentPosition(resolve, reject, {\n      enableHighAccuracy: false,\n      timeout: 10_000,\n      maximumAge: 30_000,\n      ...options,\n    });\n  });\n}\n```\n\n```js\ntry {\n  const pos = await getPosition();\n  useLocation(pos.coords);\n} catch (err) {\n  if (err.code === err.PERMISSION_DENIED) showPermissionHelp();\n  else if (err.code === err.TIMEOUT) showRetry();\n  else showGenericError();\n}\n```\n\nEvery default that bit the original snippet is now an explicit choice, and the whole thing reads top to bottom instead of nesting two callbacks inside each other.\n\n## The one real constraint: it needs a secure origin\n\nThis part isn't a trap, just worth stating plainly: every major browser requires a secure context — HTTPS, or `localhost` in development — before it will even ask for location permission. That's been true for close to a decade now, not a new restriction, and there's no workaround or flag to reach for; if a `getCurrentPosition()` call is silently failing in a way none of the above explains, check the protocol in the address bar first.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fgetcurrentposition-not-a-promise\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nThe playground above simulates the GPS fix with a slider, so you get the exact same race every time instead of waiting on whatever signal your actual device has right now. Set a slow \"fix,\" turn on \"use the default options,\" and watch the stopwatch climb with nothing to stop it — that's `timeout: Infinity`, reproduced on demand. Then try the `watchPosition()` panel: start it, hit \"simulate component unmount\" without stopping it first, and watch the counter keep climbing anyway.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fgetcurrentposition-not-a-promise\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\nThe fix for that \"spins forever\" ticket was three lines: a `timeout`, a `maximumAge`, and a `clearWatch()` in a cleanup function that had never been written. None of it was hard once someone knew to look for it — the hard part was realizing that \"sometimes it just hangs\" wasn't a flaky bug report. It was the spec's own default, read literally.\n\nDoes your codebase have a bare `getCurrentPosition()` call anywhere without a `timeout`? Worth a five-minute grep before it becomes someone else's \"impossible to reproduce\" ticket.\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [Stop Losing Promise Errors. `unhandledrejection` Catches Them.](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Funhandledrejection-swallowed-promise-errors)\n- [My .catch() Never Ran. The Bug Was Two Lines Above It](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fpromise-try-synchronous-throw-safety)\n- [JavaScript Proxy and Reflect: The Complete Guide](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-javascript-proxy-reflect)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":255,"description":256},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fgetcurrentposition-not-a-promise","A \"find my location\" button that works instantly on your laptop and hangs forever on a phone with a weak signal isn't flaky — it's getCurrentPosition() doing exactly what its defau","01a18647-e99b-71d4-a0db-6e31b5e6f414",{"id":259,"locked":18},"01a18647-e9c5-71ea-87f1-51897599070c",[261],{"id":262,"slug":46,"title":263,"_count":264},"01a18647-e9e1-7468-9139-3bd852b4328c","getCurrentPosition() and its defaults",{"questions":265},7,[267],{"locale":13,"slug":46},{"id":262,"slug":46,"title":263,"_count":269,"questionCount":265},{"questions":265},[271,273,275,279,281,285,289,293,297,299,303,307],{"slug":67,"name":68,"articles":272},167,{"slug":61,"name":65,"articles":274},134,{"slug":276,"name":277,"articles":278},"frontend","Frontend",94,{"slug":73,"name":74,"articles":280},67,{"slug":282,"name":283,"articles":284},"css","Css",50,{"slug":286,"name":287,"articles":288},"performance","Performance",28,{"slug":290,"name":291,"articles":292},"typescript","Typescript",24,{"slug":294,"name":295,"articles":296},"react","React",20,{"slug":70,"name":71,"articles":298},17,{"slug":300,"name":301,"articles":302},"browser","Browser",16,{"slug":304,"name":305,"articles":306},"node","Node",14,{"slug":308,"name":309,"articles":310},"html","Html",11,[],{"items":313,"meta":438},[314,326,344,362,381,399,417],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":315,"likeCount":19,"commentCount":19,"author":316,"vertical":317,"topic":318,"tags":319,"_count":324,"playground":325,"hasQuiz":17,"hasPlayground":17},26,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[320,321,322,323],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":327,"slug":328,"title":329,"subtitle":48,"excerpt":330,"coverUrl":331,"locale":13,"readingMinutes":51,"publishedAt":332,"viewCount":333,"likeCount":19,"commentCount":19,"author":334,"vertical":335,"topic":336,"tags":337,"_count":342,"playground":343,"hasQuiz":17,"hasPlayground":17},"01a18121-921f-706e-9714-f48280c4dc61","offscreencanvas-canvas-off-main-thread","Your Canvas Is Freezing the Page. OffscreenCanvas Fixes That.","Click a button while your canvas animation is running and the whole page stalls — because the pixels it draws, your layout, and your input handling all queue up on the same main thread. OffscreenCanvas moves the drawing itself into a worker, so the UI keeps responding no matter how much canvas work is happening.","\u002Fmedia\u002Fcovers\u002Foffscreencanvas-canvas-off-main-thread.png","2026-11-03T06:10:56.984Z",81,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[338,339,340,341],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":286,"name":287,"color":48},{"slug":276,"name":277,"color":48},{"assessments":76},{"slug":328},{"id":345,"slug":346,"title":347,"subtitle":48,"excerpt":348,"coverUrl":349,"locale":13,"readingMinutes":306,"publishedAt":350,"viewCount":351,"likeCount":19,"commentCount":19,"author":352,"vertical":353,"topic":354,"tags":355,"_count":360,"playground":361,"hasQuiz":17,"hasPlayground":17},"01a1623b-51e7-7669-89ad-eb54d67696eb","react-weekly-activity-component","React Activity Component: The State Between Render and Remount","React's Activity component hides a subtree without unmounting it — what state, effects, and renders actually do, plus a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-activity-component.png","2026-11-02T06:10:18.569Z",155,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":294,"name":295},[356,357,358,359],{"slug":294,"name":295,"color":48},{"slug":61,"name":65,"color":48},{"slug":73,"name":74,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":346},{"id":363,"slug":364,"title":365,"subtitle":48,"excerpt":366,"coverUrl":367,"locale":13,"readingMinutes":51,"publishedAt":368,"viewCount":369,"likeCount":19,"commentCount":19,"author":370,"vertical":371,"topic":372,"tags":374,"_count":379,"playground":380,"hasQuiz":17,"hasPlayground":17},"01a17bfb-4a13-77fa-bb6c-5c654aec4dd7","scrollbar-gutter-layout-shift","I Deleted My Scroll-Lock Library's Ugly Hack. `scrollbar-gutter` Replaces It.","Opening a modal shifts your whole page sideways because the scrollbar vanishes with it — the bug every scroll-lock library papers over with a JS width measurement and a padding-right hack. `scrollbar-gutter: stable` fixes it in one line now that Safari finally caught up.","\u002Fmedia\u002Fcovers\u002Fscrollbar-gutter-layout-shift.png","2026-11-02T05:31:34.059Z",117,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":282,"name":373},"CSS",[375,376,377,378],{"slug":282,"name":283,"color":48},{"slug":67,"name":68,"color":48},{"slug":276,"name":277,"color":48},{"slug":61,"name":65,"color":48},{"assessments":76},{"slug":364},{"id":382,"slug":383,"title":384,"subtitle":48,"excerpt":385,"coverUrl":386,"locale":13,"readingMinutes":51,"publishedAt":387,"viewCount":388,"likeCount":19,"commentCount":19,"author":389,"vertical":390,"topic":391,"tags":392,"_count":397,"playground":398,"hasQuiz":17,"hasPlayground":17},"01a152c8-59ef-7724-82e4-c6dc7fb6855f","pointer-events-unify-mouse-touch","You're Writing Mouse and Touch Handlers. Pointer Events Does Both.","A draggable card that works with mousedown\u002Fmousemove on desktop and breaks the moment you test it on a phone. The fix isn't a second set of touch listeners — it's deleting the mouse ones and switching to Pointer Events, which was built to handle mouse, touch, and pen as one input.","\u002Fmedia\u002Fcovers\u002Fpointer-events-unify-mouse-touch.png","2026-11-01T14:50:55.724Z",123,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[393,394,395,396],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":300,"name":301,"color":48},{"slug":276,"name":277,"color":48},{"assessments":76},{"slug":383},{"id":400,"slug":401,"title":402,"subtitle":48,"excerpt":403,"coverUrl":404,"locale":13,"readingMinutes":51,"publishedAt":405,"viewCount":406,"likeCount":19,"commentCount":19,"author":407,"vertical":408,"topic":409,"tags":410,"_count":415,"playground":416,"hasQuiz":17,"hasPlayground":17},"01a16761-d787-7313-a57c-88979dbb3a59","requestsubmit-form-validation-fix","Stop Calling form.submit(). Use requestSubmit() Instead.","A Save button living outside the \u003Cform> tag, wired to form.submit(), looks like it works — until a blank required field sails through and the page does a full reload your fetch handler never saw coming. requestSubmit() is the method that actually clicks the button for you.","\u002Fmedia\u002Fcovers\u002Frequestsubmit-form-validation-fix.png","2026-10-28T06:10:26.806Z",232,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[411,412,413,414],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":308,"name":309,"color":48},{"slug":70,"name":71,"color":48},{"assessments":76},{"slug":401},{"id":418,"slug":419,"title":420,"subtitle":48,"excerpt":421,"coverUrl":422,"locale":13,"readingMinutes":423,"publishedAt":424,"viewCount":425,"likeCount":19,"commentCount":19,"author":426,"vertical":427,"topic":428,"tags":431,"_count":436,"playground":437,"hasQuiz":17,"hasPlayground":17},"01a11b9f-f19f-7057-9a83-d09cd7fb120e","vue-weekly-vfor-directive-unmounted-hook","Vue v-for: The Directive Unmounted Hook Bug, Fixed in 3.5.41","A custom directive's unmounted hook could silently skip firing inside a keyed v-for list. The directive unmounted hook bug, Vue's real fix, and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-vfor-directive-unmounted-hook.png",13,"2026-10-26T05:39:58.065Z",276,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":429,"name":430},"vue","Vue",[432,433,434,435],{"slug":429,"name":430,"color":48},{"slug":61,"name":65,"color":48},{"slug":73,"name":74,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":419},{"page":76,"perPage":265,"total":274,"totalPages":296},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useMyLocation\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>onFound\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onFail\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getCurrentPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>pos\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">onFound\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>pos\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coords\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">onFail\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>message\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-oneline\" data-lang=\"js\">\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> enableHighAccuracy\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> timeout\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Infinity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> maximumAge\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getCurrentPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>onFound\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onFail\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  timeout\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">10_000\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  maximumAge\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">30_000\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> watchId \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">watchPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>onMove\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onError\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  enableHighAccuracy\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F later, when you're done:\n\u003C\u002Fspan>navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">clearWatch\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>watchId\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-func\">useEffect\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> id \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">watchPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>onMove\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onError\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">clearWatch\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>options\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Promise\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>resolve\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> reject\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>geolocation\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getCurrentPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>resolve\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> reject\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      enableHighAccuracy\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      timeout\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">10_000\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      maximumAge\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">30_000\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>options\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">try\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> pos \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getPosition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-func\">useLocation\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>pos\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coords\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>code \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">PERMISSION_DENIED\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-func\">showPermissionHelp\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">else\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>code \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">TIMEOUT\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-func\">showRetry\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">else\u003C\u002Fspan> \u003Cspan class=\"shj-func\">showGenericError\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":447},[]]