Eric
Eric

Reputation: 5215

Is it possible to fetch GPS location from desktop browser in the background?

I have a web app that asks the user for their GPS location. It works fine using pretty standard code that looks something like this:

function getpos_callback( a ) {
    top.location.href = 'location.php?lat=' + a.coords.latitude + '&lon=' + a.coords.longitude;
}

function error_callback( er ) {
    top.location.href = 'location.php?err=1';
}

if ( 'geolocation' in navigator ) {
    navigator.geolocation.getCurrentPosition( getpos_callback, error_callback, {
        enableHighAccuracy: true,
        maximumAge: 300000,      // 300 seconds = 5 min = max age of cached value to use
        timeout: 8000,           // 8 seconds
    } );
}

Here's the problem: on a desktop browser, it can take quite some time (4-6 seconds, and as long as 8, per the code) to determine the location. Even with mobile sometimes it can be sluggish. These seconds feel like an eternity when the user just wants to get on with using the site.

What I'd like to do is let the user in right away, but somehow "spawn a task" to ask the browser for the location in the background, and then have it pass that location to me in the background when the location is retrieved.

Is this possible?

Upvotes: 6

Views: 2695

Answers (4)

Italo Borssatto
Italo Borssatto

Reputation: 15689

The method navigator.geolocation.getCurrentPosition is asynchronous by definition. It's specified by W3C to return immediately. You can check the specification for details.

So, you can simply call the function that "let the user in" right after calling getCurrentPositionbefore having the success callback being called. The method getCurrentPosition will have already "spawn a task" in the background when you do that.

The problem in your code is that you're changing the URL of the webpage after receiving the callback, inside the getpos_callback function. I suggest you to change this behavior by updating the UI state using JS instead of reloading the page with the lat/lng parameters.

Refactoring your code, it would look like:

function getpos_callback( a ) {
    // Comment the line bellow to avoid loading the page
    // top.location.href = 'location.php?lat=' + a.coords.latitude + '&lon=' + a.coords.longitude;
    updateScreen(a.coords.latitude, a.coords.longitude);
}

function updateScreen(lat, lon) {
    // Update the UI with the lat/lon received instead of getting it from the URL
}

function error_callback( er ) {
    top.location.href = 'location.php?err=1';
}

function showUI() {
    // "let the user in right away"
}

if ( 'geolocation' in navigator ) {
    navigator.geolocation.getCurrentPosition( getpos_callback, error_callback, {
        enableHighAccuracy: true,
        maximumAge: 300000,      // 300 seconds = 5 min = max age of cached value to use
        timeout: 8000,           // 8 seconds
    } );
    showUI();
}
else { error(); }

Upvotes: 6

Jannes Botis
Jannes Botis

Reputation: 11242

No, there is no way to "spawn" a process on the background for this and let the user continue navigating=refresh the page.

The best way to do what you want is to make your web application a Single page application, meaning all actions are done by making AJAX requests.

In this case, you must only use AJAX requests for all actions:

  • submitting forms,
  • logging in,
  • visiting another view
  • etc

and then change the page html.

Submitting the location is also done via AJAX request:

function getpos_callback( a ) {
    //top.location.href = 'location.php?lat=' + a.coords.latitude + '&lon=' + a.coords.longitude;
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            console.log('done');
        }
    }
    xhr.open('GET', 'location.php?lat=' + a.coords.latitude + '&lon=' + a.coords.longitude, true);
    xhr.send(null);
}

function error_callback( er ) {
    // top.location.href = 'location.php?err=1';
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            console.log('done');
        }
    }
    xhr.open('GET', 'location.php?err=1', true);
    xhr.send(null);
}

if ( 'geolocation' in navigator ) {
    navigator.geolocation.getCurrentPosition( getpos_callback, error_callback, {
        enableHighAccuracy: true,
        maximumAge: 300000,      // 300 seconds = 5 min = max age of cached value to use
        timeout: 8000,           // 8 seconds
    } );
}

You can store the location in the session with an expired timestamp and if the user revisits the page, request it only if it has expired.

For keeping history and back button functionality for the browser, you can set the Url Hash for each view.

Upvotes: 4

Ankit
Ankit

Reputation: 1214

You can add giolocation function in document.ready and in callback function you can call your method something like this

$(document).ready(function() {
 setTimeout(() => {
  if (navigator.geolocation) {
   navigator.geolocation.getCurrentPosition(function(position) {
    alert('This will call when you have giolocation Lat is ' + position.coords.latitude + ' Lon is ' + position.coords.latitude)
   });
  } else {
   alert("Browser does not support HTML5 geolocation.");
  }
 }, 500);
 alert('Rest of things loded in in background')
});

I have added set time out for showing that site is loading while giolocation service is running in background

Here is working example https://jsfiddle.net/csbp2ey5/

Upvotes: 1

Kevin Ng
Kevin Ng

Reputation: 2174

Essentially, in my opinion, for your case, I think it is best to use the window.onload event. Of which meant that you load your geolocation after everything in the document loaded. That is because you can let the visitor browse the page while the GPS is initializing.

For spawning true thread you can use Worker but Worker does not have access the "geolocation" object of the "navigator" object. But that may change in the future.

async functions may work for your case as it does not block the main thread. However, I still think it is best to run the getCurrentPosition() after the page is loaded.

setTimeOut() is probably not the best for you. Although setTimeOut() can delay code execution and does not block the main thread when it is first called, it will still block the main thread later anyway when it is it turn to get executed.

A few reference for you:

Web workers - How do they work? - This, currently will not work for your case but who know about the future.

How to articulate the difference between asynchronous and parallel programming? - async functions are not true parallelism but may work for your case as it will not block the main thread.

How does setInterval and setTimeout work? - setTimeOut() probably is not a the right one for your usage case but worth mentioning.

https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition - The use of watchPosition. If you are into GPS, you may need this in the future.

Example window.onload code:

 <!DOCTYPE html>
<html>
<body>

<script>
function getpos_callback( a ) {
    top.location.href = 'location.php?lat=' + a.coords.latitude + '&lon=' + a.coords.longitude;
}

function error_callback( er ) {
    top.location.href = 'location.php?err=1';
}

function getGPS(){
    if ( 'geolocation' in navigator ) {
        navigator.geolocation.getCurrentPosition( getpos_callback, error_callback, {
            enableHighAccuracy: true,
            maximumAge: 300000,      // 300 seconds = 5 min = max age of cached value to use
            timeout: 8000,           // 8 seconds
        });
    }
}

/*
 * Attach getGPS to the window.onload event.
 *
 */
if ( window.addEventListener ) {
    window.addEventListener('load', getGPS);
} else {
    window.attachEvent('onload', getGPS);
}
</script>

</body>
</html> 

Upvotes: 1

Related Questions