In a plugin that I am currently authoring, I am using a function to check whether the device provides gyroscopic data. Although intuitively this can be easily resolved by checking the value of window.DeviceOrientationEvent
, i.e.:
if (window.DeviceOrientationEvent) {
// Ok, device orientation event listening is supported!
}
…this statement also resolves true when on a device with no functional gyroscope, i.e. Google Chrome on a laptop. The event data returned for alpha, beta and gamma are, however, null
. Basically, the window.DeviceOrientationEvent
does not tell me if there is gyroscopic data available, but only informs me if the browser/user-agent supports the event.
Therefore, not only do I have to check for support for the event, but also have to cross verify the value of alpha, beta or gamma such that they are not null. The best way I could check is using promises, which I feel a bit cumbersome and long-winded:
var check = {
// Does it have a working gyroscope?
gyroscope: function() {
var d = new $.Deferred(),
handler = function(e) {
d.resolve({
alpha: e.alpha,
beta: e.beta,
gamma: e.gamma
});
// Listen to device orientation once and remove listener immediately
window.removeEventListener('deviceorientation', handler, false);
};
window.addEventListener('deviceorientation', handler, false);
return d;
}
}
// Wait for gyroscope data
$.when(check.gyroscope()).done(function(r) {
if(r.alpha === null && r.beta === null && r.gamma === null) {
console.log('Device has no functional gyroscope.');
} else {
console.log('Device has functional gyroscope.')
}
});
I am wondering if there is a cleaner way to write this code?
-
1\$\begingroup\$ For reference, here's a library that seems to do much of the same, and is cross-browser (i.e. supports events with vendor prefixes) \$\endgroup\$Flambino– Flambino2015年05月08日 22:45:07 +00:00Commented May 8, 2015 at 22:45
-
\$\begingroup\$ @Flambino Wow, didn't know that library existed — should've googled a tad bit harder ;) \$\endgroup\$Terry– Terry2015年05月08日 23:47:45 +00:00Commented May 8, 2015 at 23:47
-
\$\begingroup\$ Well, I don't know if it's any good - just stumbled across it. I skimmed the code though, and it seems pretty neat and well-written. Figured you'd be interested. \$\endgroup\$Flambino– Flambino2015年05月09日 07:29:37 +00:00Commented May 9, 2015 at 7:29
1 Answer 1
I imagine you're still checking
window.DeviceOrientationEvent
? Because if that doesn't exist, you can presumably skip everything else.When using
$.Deferred
you should always return its.promise()
- not the deferred object itself. The deferred object itself is "read/write" (i.e. you can callresolve
andreject
on it), while the promise it creates is "read-only" which is what you want to pass on to other code.You don't need
$.when
unless you're waiting for several promises to resolve. You can simple callpromise.done()
(or.then
or.fail
or.always
)Since the idea is get a yes/no answer, I'd simply use the resolve/reject callbacks of the deferred and do all the checking in one place.
I don't know if it's relevant, but checking strictly against
null
will give you a false positive ifalpha
etc.are actually completelyundefined
. It might be better to checktypeof alpha === 'number'
.Of course, you don't really need promises; a simple callback would do.
For instance:
var check = {
gyroscope: function (callback) {
function handler(event) {
var hasGyro = typeof event.alpha === 'number'
&& typeof event.beta === 'number'
&& typeof event.gamma === 'number';
window.removeEventListener('deviceorientation', handler, false);
callback(hasGyro);
}
window.addEventListener('deviceorientation', handler, false);
}
};
check.gyroscope(function (hasGyroscope) {
if(hasGyroscope) {
console.log('Device has functional gyroscope.')
} else {
console.log('Device has no functional gyroscope.');
}
});
If you do want to use $.Deferred
, I'd recommend something like:
var check = {
gyroscope: function () {
var deferred = $.Deferred();
function handler(event) {
var hasGyro = typeof event.alpha === 'number'
&& typeof event.beta === 'number'
&& typeof event.gamma === 'number';
window.removeEventListener('deviceorientation', handler, false);
hasGyro ? deferred.resolve() : deferred.reject();
}
window.addEventListener('deviceorientation', handler, false);
return deferred.promise();
}
};
check.gyroscope().then(function () {
console.log('Device has functional gyroscope.')
}, function () {
console.log('Device has no functional gyroscope.');
});
-
\$\begingroup\$ Thanks for the useful input! I have to admit that I have unintentionally left out some important information—
$.when()
was used to check for other promises, too, and two of which are AJAX calls; and that I did do a basicwindow.DeviceOrientationEvent
check, but left it out when I was reducing the code. And thanks for pointing out that I forgot to return the promise... and surprised that it still works when I forget to do so. \$\endgroup\$Terry– Terry2015年05月09日 10:14:31 +00:00Commented May 9, 2015 at 10:14 -
\$\begingroup\$ I learn from this, thanks. But something puzzles me: why to have a "double envelope"
var check = { gyroscope: function() {...} };
instead of something more simple likevar checkGyro = function() {...};
? \$\endgroup\$cFreed– cFreed2015年05月09日 12:47:53 +00:00Commented May 9, 2015 at 12:47 -
1\$\begingroup\$ @cFreed True, a plain function would work just as well. I just assumed that there might be other functions in that
check
namespace/object which were just left out in the question. So I just kept that structure intact, and focussed on the content. \$\endgroup\$Flambino– Flambino2015年05月09日 13:10:37 +00:00Commented May 9, 2015 at 13:10
Explore related questions
See similar questions with these tags.