【问题标题】:NativeScript - Geolocation: right way to use getCurrentLocation promise functionNativeScript - 地理位置:使用 getCurrentLocation 承诺功能的正确方法
【发布时间】:2017-11-11 12:04:35
【问题描述】:

我正在编写一个使用nativescript-geolocation API 的简单应用程序。 函数 getCurrentLocation 基本上可以正常工作,但是当我移动到另一个名为 maps-module.js 的文件并从文件 detail.js 的主线程中调用它时,它返回的对象位置为 NULL。 在打印到控制台对象之后,我意识到在函数完成查找位置之前返回了变量 returned_location。 我认为它的多线程问题,但我真的不知道如何解决它。 这是我的文件。

detail.js

var Frame = require("ui/frame");
var Observable = require("data/observable");

var MapsModel = require("../../view-models/maps-model");


var defaultMapInfo = new MapsModel({
    latitude: "10.7743332",
    longitude: "106.6345204",
    zoom: "0",
    bearing: "0",
    tilt: "0",
    padding: "0"
});

var page;
var mapView;

exports.pageLoaded = function(args) {
    page = args.object;
    var data = page.navigationContext;
    page.bindingContext = defaultMapInfo;
}

exports.onBackTap = function () {
    console.log("Back to home");
    var topmost = Frame.topmost();
    topmost.goBack();
}

function onMapReady(args) {
    mapView = args.object;
    mapView.settings.zoomGesturesEnabled = true;
}

function onMarkerSelect(args) {
    console.log("Clicked on " + args.marker.title);
}

function onCameraChanged(args) {
    console.log("Camera changed: " + JSON.stringify(args.camera)); 
}

function getCurPos(args) {
    var returned_location = defaultMapInfo.getCurrentPosition(); // variable is returned before function finished
    console.dir(returned_location);
}


exports.onMapReady = onMapReady;
exports.onMarkerSelect = onMarkerSelect;
exports.onCameraChanged = onCameraChanged;
exports.getCurPos = getCurPos;

ma​​ps-module.js

var Observable = require("data/observable");

var Geolocation = require("nativescript-geolocation");
var Gmap = require("nativescript-google-maps-sdk");

function Map(info) {
    info = info || {};
    var _currentPosition;

    var viewModel = new Observable.fromObject({
        latitude: info.latitude || "",
        longitude: info.longitude || "",
        zoom: info.zoom || "",
        bearing: info.bearing || "",
        tilt: info.bearing || "",
        padding: info.padding || "",
    });

    viewModel.getCurrentPosition = function() {
        if (!Geolocation.isEnabled()) {
            Geolocation.enableLocationRequest();
        }

        if (Geolocation.isEnabled()) {
            var location = Geolocation.getCurrentLocation({
                desiredAccuracy: 3, 
                updateDistance: 10, 
                maximumAge: 20000, 
                timeout: 20000
            })
            .then(function(loc) {
                if (loc) {
                    console.log("Current location is: " + loc["latitude"] + ", " + loc["longitude"]);
                    return Gmap.Position.positionFromLatLng(loc["latitude"], loc["longitude"]);
                }
            }, function(e){
                console.log("Error: " + e.message);
            });

            if (location)
                console.dir(location);
        }
    }

    return viewModel;
}

module.exports = Map;

【问题讨论】:

    标签: javascript android promise geolocation nativescript


    【解决方案1】:

    如果 Shiva Prasad 的脚注 ...

    “geolocation.enableLocationRequest() 也是异步方法”

    ... 是正确的,那么geolocation.enableLocationRequest() 返回的 Promise 必须得到适当的处理,代码会发生相当大的变化。

    试试这个:

    viewModel.getCurrentPosition = function(options) {
        var settings = Object.assign({
            'desiredAccuracy': 3,
            'updateDistance': 10,
            'maximumAge': 20000,
            'timeout': 20000
        }, options || {});
    
        var p = Promise.resolve() // Start promise chain with a resolved native Promise.
        .then(function() {
            if (!Geolocation.isEnabled()) {
                return Geolocation.enableLocationRequest(); // return a Promise
            } else {
                // No need to return anything here.
                // `undefined` will suffice at next step in the chain.
            }
        })
        .then(function() {
            if (Geolocation.isEnabled()) {
                return Geolocation.getCurrentLocation(settings); // return a Promise
            } else { // <<< necessary to handle case where Geolocation didn't enable.
                throw new Error('Geolocation could not be enabled');
            }
        })
        .then(function(loc) {
            if (loc) {
                console.log("Current location is: " + loc.latitude + ", " + loc.longitude);
                return Gmap.Position.positionFromLatLng(loc.latitude, loc.longitude);
            } else { // <<< necessary to handle case where loc was not derived.
                throw new Error('Geolocation enabled, but failed to derive current location');
            }
        })
        .catch(function(e) {
            console.error(e);
            throw e; // Rethrow the error otherwise it is considered caught and the promise chain will continue down its success path.
            // Alternatively, return a manually-coded default `loc` object.
        });
    
        // Now race `p` against a timeout in case enableLocationRequest() hangs.
        return Promise.race(p, new Promise(function(resolve, reject) {
            setTimeout(function() {
                reject(new Error('viewModel.getCurrentPosition() timed out'));
            }, settings.timeout);
        }));
    }
    return viewModel;
    

    注意事项:

    1. 使用已解析的原生 Promise 启动链与包装 new Promise(...) 的效果大致相同,但更简洁主要是因为链中的意外抛出可以保证将 Error 对象传递到链的错误路径中,而无需try/catch/reject()。另外,在标有“return a Promise”的两行中,我们不需要关心我们是返回 Promise 还是返回值;两者都将被原生 Promise 链同化。

    2. 包含两个else 子句以应对不会自动抛出的故障情况。

    3. Promise.race() 不是必需的,但可以防止here 报告的问题。内置的“超时”机制可能就足够了。这种额外的超时机制是一种“束手无策”的措施。

    4. 包含一种机制,通过传递 options 对象来覆盖 viewModel.getCurrentPosition 中的硬编码默认值。要使用默认值运行,只需调用 viewModel.getCurrentPosition()。引入此功能的主要目的是允许 settings.timeoutPromise.race() 中重复使用。

    编辑:

    感谢@grantwparks 提供Geolocation.isEnabled() 也返回Promise 的信息。

    所以现在我们可以使用p = Geolocation.isEnabled().... 启动 Promise 链并测试异步传递的布尔值。如果false 则尝试启用。

    从那时起,如果地理定位最初启用或已启用,则将遵循 Promise 链的成功路径。启用地理定位的进一步测试消失了。

    这应该可行:

    viewModel.getCurrentPosition = function(options) {
        var settings = Object.assign({
            'desiredAccuracy': 3,
            'updateDistance': 10,
            'maximumAge': 20000,
            'timeout': 20000
        }, options || {});
    
        var p = Geolocation.isEnabled() // returned Promise resolves to true|false.
        .then(function(isEnabled) {
            if (isEnabled) {
                // No need to return anything here.
                // `undefined` will suffice at next step in the chain.
            } else {
                return Geolocation.enableLocationRequest(); // returned Promise will cause main chain to follow success path if Geolocation.enableLocationRequest() was successful, or error path if it failed;
            }
        })
        .then(function() {
            return Geolocation.getCurrentLocation(settings); // return Promise
        })
        .then(function(loc) {
            if (loc) {
                console.log("Current location is: " + loc.latitude + ", " + loc.longitude);
                return Gmap.Position.positionFromLatLng(loc.latitude, loc.longitude);
            } else { // <<< necessary to handle case where loc was not derived.
                throw new Error('Geolocation enabled, but failed to derive current location');
            }
        })
        .catch(function(e) {
            console.error(e);
            throw e; // Rethrow the error otherwise it is considered caught and the promise chain will continue down its success path.
            // Alternatively, return a manually-coded default `loc` object.
        });
    
        // Now race `p` against a timeout in case Geolocation.isEnabled() or Geolocation.enableLocationRequest() hangs.
        return Promise.race(p, new Promise(function(resolve, reject) {
            setTimeout(function() {
                reject(new Error('viewModel.getCurrentPosition() timed out'));
            }, settings.timeout);
        }));
    }
    return viewModel;
    

    【讨论】:

    • Shiva Prasad 的回答和你的回答就像魅力一样。我也更了解 Promise 和处理异步,非常感谢 XD。
    • 两个答案之间的差异会在失败的情况下出现。例如。在禁用地理定位的设备上进行测试。
    • isEnabled 在当前版本中也返回一个 Promise。
    • @grantwparks,哎哟!这又改变了一切。
    • 编辑确认Geolocation.isEnabled()返回Promise。
    【解决方案2】:

    由于获取位置是一个异步过程,因此您的 viewModel.getCurrentPosition 应该返回一个承诺,并且看起来像这样,

    viewModel.getCurrentPosition() {
        return new Promise((resolve, reject) => {
            geolocation
                .getCurrentLocation({
                    desiredAccuracy: enums.Accuracy.high,
                    updateDistance: 0.1,
                    maximumAge: 5000,
                    timeout: 20000
                })
                .then(r => {
                    resolve(r);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }
    

    然后当你使用它时,它会是这样的

    defaultMapInfo.getCurrentPosition()
        .then(latlng => {
           // do something with latlng {latitude: 12.34, longitude: 56.78}
        }.catch(error => {
           // couldn't get location
        }
    }
    

    希望对你有帮助:)

    更新:顺便说一句,geolocation.enableLocationRequest() 也是一个异步方法。

    【讨论】:

    • 这就是我要找的,我的代码现在简单多了,谢谢XD
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    • 1970-01-01
    • 2016-06-01
    • 2017-10-18
    • 1970-01-01
    • 2021-07-15
    • 2013-10-06
    相关资源
    最近更新 更多