I have this module that wraps a call to an asynchronous method to watch for changes in the browser's geolocation navigator.geolocation.watchPosition
var geo_value;
//Initialises the devices geolocation
navigator.geolocation.watchPosition(function (position) {
geo_value = position.coords.longitude + "," + position.coords.latitude;
});
Everytime the watchPosition method call fires, I store the value in a variable to provide me programmatic access to the currentPosition at any given time.
This all works fine, however I now want to expose this currentPosition via a custom async method that I am exporting from my module.
define(["global"], function (window) {
var navigator = window.navigator;
var geo_value;
//Initialises the devices geolocation
navigator.geolocation.watchPosition(function (position) {
geo_value = position.coords.longitude + "," + position.coords.latitude;
});
return {
currentPosition: function (callback) {
setTimeout(function () {
while (!geo_value) {
//keep looping until geo_value has been initially set by the watch position above
}
//geo_value has been set, execute callback
callback(geo_value);
}, 0);
}
};
});
I have made the module expose the currentPosition, which works if geo_value has already been set, but I also want it to wait asynchronously if geo_value hasn't been set and then execute the callback when the watchPosition finally sets a value for geo_value. I tried using the setTimeout function to attempt at making it asynchronous, but it didn't work.
Please any help will be much appreciated.
3 Answers 3
So, Javascript is really a single-threaded event loop. Getting a feel for what exactly that means will take experience.
In this case, it means that your while loop will never terminate, because nothing else can happen while it's running.
One better way to do this is to call the callbacks you're getting as params to currentPosition when watchPosition is called.
define(["global"], function (window) {
var navigator = window.navigator,
geo_value,
callbacks = [],
//Initialises the devices geolocation
navigator.geolocation.watchPosition(function (position) {
geo_value = position.coords.longitude + "," + position.coords.latitude;
// Update all callbacks with new value.
for (var i = 0; i < callbacks.length; i++) {
callbacks[i](geo_value);
}
});
return {
onCurrentPosition: function (callback) {
callbacks.push(callback)
}
};
});
4 Comments
geo_value right now. If geo_value exists, you should call the callback right away AND push it in the callbacks. Also as far as I know, the OP was not interested in getting notified for every subsequent position changes, so once you have a position, you do not need to push any callbacks, just return the current one.currentPosition and stop pushing new callbacks once geo_value has a value, instead just call callback(geo_value) right away. @closure's solution will fail if currentPosition is called multiple times before geo_value has a value.define(["global"], function (window) {
var navigator = window.navigator;
var geo_value;
var cb;
//Initialises the devices geolocation
navigator.geolocation.watchPosition(function (position) {
geo_value = position.coords.longitude + "," + position.coords.latitude;
if (cb) {
cb(geo_value);
cb = null;
}
});
return {
currentPosition: function (callback) {
if (geo_value) {
callback(geo_value);
} else {
cb = callback;
}
};
});
1 Comment
currentPosition multiple times before geo_value has a value?This code below blocks other code to be executed as JavaScript only has one thread of execution:
while (!geo_value) {
//keep looping until geo_value has been initially set by the watch position above
}
Instead try something like this:
currentPosition: function(callback) {
if (geo_value) {
callback(geo_value);
} else {
// try again 1 sec later
setTimeout(this.currentPosition.bind(this, callback), 1000);
}
}