Knockout视图不会使用geoJSON更新根据Google地图的界限过滤
此脚本是一个Web应用程序的一部分,该应用程序允许用户在Google Map上显示有关地震的信息。该脚本异步加载Google地图并创建Knockout ViewModel。用户可以通过一些下拉菜单选择要显示的地震,这些下拉菜单绑定到Knockout Observables(self.curFeedType
和self.curFeedTimeHorizon
)。 该应用程序将在地图旁边显示地震标题列表,筛选出不在地图视口范围内的地震标题。地震数据通过USGS.gov的AJAX请求加载。Knockout视图不会使用geoJSON更新根据Google地图的界限过滤
我希望显示的地震标题列表在任何一种饲料类型更新后立即更新---目前无法使用。我相信这是一个异步问题---当我使用调试器时,它可以工作,但反之亦然。
我知道大部分功能都能正常工作,因为有一个单独的侦听器在地图边界发生更改时更新UI。但是,现在,地震标题列表将不会更新,直到更改了馈送并移动了地图为止。
function ControlViewModel() {
var self = this;
self.map = null;
self.loadedQuakes = [];
self.visibleQuakes = ko.observableArray();
// types of available earthquake feeds from USGS.gov
self.feedTypes = ["significant", "4.5", "2.5", "1.0", "all"];
self.feedTimeHorizons = ["hour", "day", "week", "month"];
// use significant and week as default feed when app loads
self.curFeedType = ko.observable("significant");
self.curFeedTimeHorizon = ko.observable("week");
function setVisibleQuakes (bounds, quakesToFilter) {
self.visibleQuakes(quakesToFilter.filter(quake => {
return bounds.contains(quake.latLon);
}));
}
self.updateVisibleQuakes = function(bounds, loadedQuakes) {
loadedQuakes ? setVisibleQuakes(bounds, loadedQuakes) :
setVisibleQuakes(bounds, self.loadedQuakes);
}
// Generate a url for the desired earthquake feed
self.generateFeedUrl = function() {
let baseFeedUrl = `https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/${self.curFeedType()}_${self.curFeedTimeHorizon()}.geojson`;
return baseFeedUrl;
}
// load quakes from USGS and create new model objects
async function getQuakeFeed() {
let loadedQuakes = [];
$.getJSON(self.generateFeedUrl(), function(data) {
for (var i = 0; i < data.features.length; i++) {
loadedQuakes.push(new earthQuakeModel(data.features[i]));
};
});
return loadedQuakes;
}
self.updateQuakeFeed = async function() {
if (self.map) {
let loadedQuakes = getQuakeFeed();
loadedQuakes.then(result => {
self.updateVisibleQuakes(self.map.getBounds(), result);
self.loadedQuakes = result;
});
}
}
// update the feed when either select menu changes
self.curFeedType.subscribe(self.updateQuakeFeed, null);
self.curFeedTimeHorizon.subscribe(self.updateQuakeFeed, null);
// call for inital setup
self.updateQuakeFeed();
}
var controlViewModel = new ControlViewModel();
// create a new Google Map
function initMap() {
let map = new google.maps.Map(document.getElementById('map_container'), {
center: {lat: 0, lng: 0},
zoom: 3
});
console.log(map);
controlViewModel.map = map;
// listener to let UI know that map bounds have changed
map.addListener('idle', function() {
let bounds = map.getBounds();
controlViewModel.updateVisibleQuakes(bounds, null);
});
}
ko.applyBindings(controlViewModel);
既然问了这个问题,我已经想出了答案。
以下功能使得AJAX请求:
// load quakes from USGS and create new model objects
async function getQuakeFeed() {
let loadedQuakes = [];
$.getJSON(self.generateFeedUrl(), function(data) {
for (var i = 0; i < data.features.length; i++) {
loadedQuakes.push(new earthQuakeModel(data.features[i]));
};
});
return loadedQuakes;
}
即使这个函数声明为异步函数,隐式地包裹在一个无极的返回值,它不会等待之前立即空数组返回,在AJAX请求完成之前。因此,承诺立即“履行”,导致其余链条在self.updateQuakeFeed()
中失败。
我通过立即包裹在一个承诺的AJAX请求并等待纠正了这个问题,它在继续之前必须满足:
async function getQuakeFeed() {
return $.getJSON(self.generateFeedUrl());
}
self.populateQuakeModel = async function() {
let feedResults = await getQuakeFeed();
let newQuakes = [];
feedResults.features.forEach(feature => {
newQuakes.push(new earthQuakeModel(feature));
});
return newQuakes;
}
self.updateQuakeFeed = async function() {
if (self.map) {
let newQuakes = await self.populateQuakeModel();
self.loadedQuakes = newQuakes;
self.updateVisibleQuakes(self.map.getBounds());
}
}
从谷歌下面的指南是这出有用: Promises "Compatibility with other libraries"其中讨论在实际的ES6 Promise对象中立即包装jQuery的Deferred --- the wrapper of return type from jQuery.ajax ---的值。