Knockout视图不会使用geoJSON更新根据Google地图的界限过滤

问题描述:

此脚本是一个Web应用程序的一部分,该应用程序允许用户在Google Map上显示有关地震的信息。该脚本异步加载Google地图并创建Knockout ViewModel。用户可以通过一些下拉菜单选择要显示的地震,这些下拉菜单绑定到Knockout Observables(self.curFeedTypeself.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 ---的值。