使用MapBox Geocoding API获取JSON数据时出现undefined错误求助
问题
我编写了一段JavaScript脚本,调用MapBox Geocoding API获取两个城市(城市+州)的JSON数据,以此创建包含这两个城市标记的GeoJSON FeatureCollection。直接使用原始坐标时标记可正常显示,但使用API返回的数据时,控制台提示undefined is not an object (evaluating 'userData.features[0]'),尽管控制台已打印出存在的对象数据。
代码
<div id='map' style='width: 300px; height: 200px;'></div> <script> mapboxgl.accessToken = 'MY_ACCESS_TOKEN'; var map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v11', center: [-96, 37.8], zoom: 3 }); var userData = {}; function saveUserData(input) { userData = input; console.log(userData); } var partnerData = {}; function savePartnerData(input) { partnerData = input; console.log(partnerData); } fetch('https://api.mapbox.com/geocoding/v5/mapbox.places/<%= @user.location.strip %>.json?limit=1&access_token=MY_ACCESS_TOKEN') .then(res => res.json()) .then(data => saveUserData(data)); fetch('https://api.mapbox.com/geocoding/v5/mapbox.places/<%= prayer_partner.location.strip %>.json?limit=1&access_token=MY_ACCESS_TOKEN') .then(res => res.json()) .then(data => savePartnerData(data)); const geojson = { type: 'FeatureCollection', features: [ { type: 'Feature', geometry: { type: 'Point', coordinates: userData.features[0].geometry.coordinates }, properties: { title: '<%= @user.name %>', description: '<%= @user.location %>' } }, { type: 'Feature', geometry: { type: 'Point', coordinates: partnerData.features[0].geometry.coordinates }, properties: { title: '<%= prayer_partner.name %>', description: '<%= prayer_partner.location %>' } } ] }; // add markers to map for (const feature of geojson.features) { // create a HTML element for each feature const el = document.createElement('div'); el.className = 'marker'; // make a marker for each feature and add to the map new mapboxgl.Marker(el).setLngLat(feature.geometry.coordinates) .setPopup( new mapboxgl.Popup({ offset: 25 }) // add popups .setHTML( `<h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>` ) ).addTo(map); } </script>
控制台日志
[Error] TypeError: undefined is not an object (evaluating 'userData.features[0]') Global Code (1:175) [Log] {type: "FeatureCollection", query: ["madison", "wi"], features: Array, attribution: "NOTICE: © 2022 Mapbox and its suppliers. All right…y not be retained. POI(s) provided by Foursquare."} (1, line 150) [Log] {type: "FeatureCollection", query: ["minot", "nd"], features: Array, attribution: "NOTICE: © 2022 Mapbox and its suppliers. All right…y not be retained. POI(s) provided by Foursquare."} (1, line 157)
解决方案
问题根源
fetch是异步操作,代码执行顺序导致了报错:
- 初始化
userData和partnerData为空对象 - 发起两个
fetch请求(此时请求在后台处理,未返回数据) - 立即创建
geojson对象,这时userData和partnerData还是空对象,没有features属性,因此触发报错 - 后续
fetch请求完成,控制台打印出数据,但此时geojson已经创建失败
修复步骤
必须等两个fetch请求全部完成后,再创建GeoJSON并添加标记。可以用Promise.all来处理多个异步请求:
- 将两个
fetch请求包装为Promise,通过Promise.all等待它们全部完成 - 在所有请求完成的回调中,处理返回数据、创建GeoJSON并添加标记
修复后的代码
<div id='map' style='width: 300px; height: 200px;'></div> <script> mapboxgl.accessToken = 'MY_ACCESS_TOKEN'; var map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v11', center: [-96, 37.8], zoom: 3 }); // 定义两个fetch请求的Promise const userFetch = fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/<%= @user.location.strip %>.json?limit=1&access_token=${mapboxgl.accessToken}`) .then(res => res.json()); const partnerFetch = fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/<%= prayer_partner.location.strip %>.json?limit=1&access_token=${mapboxgl.accessToken}`) .then(res => res.json()); // 等待两个请求都完成 Promise.all([userFetch, partnerFetch]).then(([userData, partnerData]) => { // 校验数据是否有效 if (!userData.features || userData.features.length === 0 || !partnerData.features || partnerData.features.length === 0) { console.error('获取地址数据失败'); return; } const geojson = { type: 'FeatureCollection', features: [ { type: 'Feature', geometry: { type: 'Point', coordinates: userData.features[0].geometry.coordinates }, properties: { title: '<%= @user.name %>', description: '<%= @user.location %>' } }, { type: 'Feature', geometry: { type: 'Point', coordinates: partnerData.features[0].geometry.coordinates }, properties: { title: '<%= prayer_partner.name %>', description: '<%= prayer_partner.location %>' } } ] }; // 添加标记到地图 for (const feature of geojson.features) { const el = document.createElement('div'); el.className = 'marker'; new mapboxgl.Marker(el) .setLngLat(feature.geometry.coordinates) .setPopup( new mapboxgl.Popup({ offset: 25 }) .setHTML(`<h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>`) ) .addTo(map); } }).catch(err => { console.error('请求出错:', err); }); </script>
额外优化点
- 统一用变量引用accessToken,避免重复编写
- 添加数据有效性校验,防止API返回空数据或请求失败
- 使用模板字符串拼接URL,提升代码可读性
内容的提问来源于stack exchange,提问作者Jacob Jackson
相关产品推荐
相关产品推荐

