You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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是异步操作,代码执行顺序导致了报错:

  1. 初始化userData和partnerData为空对象
  2. 发起两个fetch请求(此时请求在后台处理,未返回数据)
  3. 立即创建geojson对象,这时userData和partnerData还是空对象,没有features属性,因此触发报错
  4. 后续fetch请求完成,控制台打印出数据,但此时geojson已经创建失败

修复步骤

必须等两个fetch请求全部完成后,再创建GeoJSON并添加标记。可以用Promise.all来处理多个异步请求:

  1. 将两个fetch请求包装为Promise,通过Promise.all等待它们全部完成
  2. 在所有请求完成的回调中,处理返回数据、创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:40:49