Global Variable Not Working In Javascript

In the following code I make two FETCHs to a URL. The first one returns a URL and the second one tries to do a FETCH on that URL. In the first FETCH I store the URL retiurned to a what I think is a global variable “observationsURL”. However when the code executes the second FETCH it says that the variable is not defined and errors off. I have tried putting the VAR declaration at the very top of the code but that also produces errors. What am I missing? I understand it is a scope problem but not sure how to proceed. Thanks!

var ObservationDataURL = "";

$(document).ready(function() {
    $("#searchInput").on("keyup", function(e) {
        var cityname = e.target.value;
        const APIKey = "...";

        var firstURL = "https://api.weather.gov/points/44.631,-86.0725";

        // Make a request to the server
        $.ajax({
            url:firstURL,
        }).done(function(weatherdata) {
            console.log(weatherdata.properties.relativeLocation.properties.city);
            var cityName = weatherdata.properties.relativeLocation.properties.city

            ObservationDataURL = weatherdata.properties.forecastGridData;
            console.log(ObservationDataURL);

            var targetdataURL = ObservationDataURL + "/object/properties/temperature/values/value";
            console.log("Target data URL is:");
            console.log(targetdataURL);
            });
        });
    })


        let addr3 = 'https://api.weather.gov/points/44.630978,-86.072480';

        fetch(addr3)
        .then(data => data.json())
        .then(json => {
            console.log(json);
            // do stuff with json data here

            var forecastURL = addr3;
            var observationsURL = "";
            observationsURL = json.properties.observationStations;
            console.log(forecastURL);
            console.log(observationsURL);

        })
        .catch(console.log);


        fetch(observationsURL)
        .then(data => data.json())
        .then(json => {
            console.log("New stuff");
            console.log(json);
        })
        .catch(console.log);

observationsURL is not a global. ObservationDataURL is a global. You’re writing to the wrong variable.

EDIT: Sorry, i’m mixing your first and second fetches up.

        fetch(addr3)
        .then(data => data.json())
        .then(json => {
            console.log(json);
            // do stuff with json data here

            var forecastURL = addr3;
            var observationsURL = "";
            observationsURL = json.properties.observationStations;
            console.log(forecastURL);
            console.log(observationsURL);

        })
        .catch(console.log);

observationsURL here is NOT a global. It’s a local to the then.

is then referencing a nonexistant variable.

You also seem to be a bit confused as to how and when asynchronous code is executed. For example in the following which is based roughly on your code

const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';
let observationsURL = 'undefined-url';

fetch(weatherApiUrl)
  .then(data => data.json())
  .then(json => {
    // set the global here
    observationsURL = json.properties.observationStations;
    console.log(observationsURL); // https://api.weather.gov/gridpoints/APX/14,39/stations
  })
  .catch(console.log);

console.log(observationsURL); // undefined-url

The console.log(observationsURL) will output 'undefined-url', not 'https://api.weather.gov/gridpoints/APX/14,39/stations'

The following line inside the thenable is executed after the console.log

observationsURL = json.properties.observationStations;

Therefore the same will apply when you try a to fetch(observationsURL)

Edit 1: You could ammend this code with a nested fetch.

const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';

fetch(weatherApiUrl)
  .then(data => data.json())
  .then(json => {
    fetch(json.properties.observationStations)
      .then(data => data.json())
      .then(console.log)
  })
  .catch(console.error);

Edit 2: Or using async/await

async function fetchJson (url) {
  const response = await fetch(url)
  return response.json()
}

async function getWeatherData() {
  const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';
  const weatherApiData = await fetchJson(weatherApiUrl)
  
  const observationStationsUrl = weatherApiData.properties.observationStations
  const observationData = await fetchJson(observationStationsUrl)
  
  console.log(observationData)
}

getWeatherData()
3 Likes

Thanks. I see what you are trying to do. I tried both with the same results - It does open the second URL and in my browser I can see the observationStations data is there but the call to it in the code doesn’t produce anything other than the base URL. I am looking to read observationStations[0] into a variable so that I can make another call to open that and read the temperature field among others.

I’m not sure I fully understand, but something like this?

async function fetchJson (url) {
  const response = await fetch(url)
  return response.json()
}

async function getWeatherData() {
  const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';
  const weatherApiData = await fetchJson(weatherApiUrl)
  
  const observationStationsUrl = weatherApiData.properties.observationStations
  // fetch the observationStations array property
  const { observationStations } = await fetchJson(observationStationsUrl)

  // fetch the data from the url in the first index of observationStations array
  const firstStationData = await fetchJson(observationStations[0])
  console.log(JSON.stringify(firstStationData, null, 2))
}

getWeatherData()

This outputs the following.

"{
  '@context': [
    'https://geojson.org/geojson-ld/geojson-context.jsonld',
    {
      '@version': '1.1',
      'wx': 'https://api.weather.gov/ontology#',
      's': 'https://schema.org/',
      'geo': 'http://www.opengis.net/ont/geosparql#',
      'unit': 'http://codes.wmo.int/common/unit/',
      '@vocab': 'https://api.weather.gov/ontology#',
      'geometry': {
        '@id': 's:GeoCoordinates',
        '@type': 'geo:wktLiteral'
      },
      'city': 's:addressLocality',
      'state': 's:addressRegion',
      'distance': {
        '@id': 's:Distance',
        '@type': 's:QuantitativeValue'
      },
      'bearing': {
        '@type': 's:QuantitativeValue'
      },
      'value': {
        '@id': 's:value'
      },
      'unitCode': {
        '@id': 's:unitCode',
        '@type': '@id'
      },
      'forecastOffice': {
        '@type': '@id'
      },
      'forecastGridData': {
        '@type': '@id'
      },
      'publicZone': {
        '@type': '@id'
      },
      'county': {
        '@type': '@id'
      }
    }
  ],
  'id': 'https://api.weather.gov/stations/KMBL',
  'type': 'Feature',
  'geometry': {
    'type': 'Point',
    'coordinates': [
      -86.23773,
      44.27142
    ]
  },
  'properties': {
    '@id': 'https://api.weather.gov/stations/KMBL',
    '@type': 'wx:ObservationStation',
    'elevation': {
      'unitCode': 'wmoUnit:m',
      'value': 185.928
    },
    'stationIdentifier': 'KMBL',
    'name': 'Manistee County - Blacker Airport',
    'timeZone': 'America/Detroit',
    'forecast': 'https://api.weather.gov/zones/forecast/MIZ031',
    'county': 'https://api.weather.gov/zones/county/MIC101',
    'fireWeatherZone': 'https://api.weather.gov/zones/fire/MIZ031'
  }
}"

Edit:

If you want to do this with thenables instead

const fetchJson = async (url) => (await fetch(url)).json()

fetchJson('https://api.weather.gov/points/44.630978,-86.072480')
  .then((data) => fetchJson(data.properties.observationStations))
  .then((data) => fetchJson(data.observationStations[0]))
  .then((data) => console.log(JSON.stringify(data, null, 2)))
  .catch(console.error)

Insert standard warnings about null references.

Also, if you’re using a fixed point,why do the first two fetches? Surely the observation station doesn’t… change? At least not with any great frequency?

1 Like

Also, if you’re using a fixed point,why do the first two fetches? Surely the observation station doesn’t… change? At least not with any great frequency?

So something along these lines?

const fetchJson = async (url) => {
  const response = await fetch(url)
 
  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }
  
  return response.json();
}

// skip straight to the observation station url
fetchJson('https://api.weather.gov/stations/KMBL')
  .then((data) => console.log(JSON.stringify(data, null, 2)))
  .catch((err) => console.error(`Fetch problem: ${err.message}`))

Thank you so much! I solved it with this code which fetches the closest station’s temp. I have to make so many calls because I will have an input box for City and State and need to convert that to lattitude and longitude (yet to do) and then find the nearest reporting station to that point. This code does that!

async function fetchJson (url) {
    const response = await fetch(url)
    return response.json()
  }
  
async function getWeatherData() {
  const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';
  const weatherApiData = await fetchJson(weatherApiUrl)
  
  const observationStationsUrl = weatherApiData.properties.observationStations
  // fetch the observationStations array property
  const { observationStations } = await fetchJson(observationStationsUrl)
  
  // fetch the data from the url in the first index of observationStations array
  const firstStationData = await fetchJson(observationStations[0])
  closestStation = firstStationData.properties.stationIdentifier;
  console.log("Closest reporting station = " + closestStation);
  // console.log(JSON.stringify(firstStationData, null, 2))

  var closestObservations = "https://api.weather.gov/stations/" + closestStation + "/observations/latest";
  const { results } = await fetchJson(closestObservations);
  console.log("Closest reporting URL = " + closestObservations);
  // console.log(closestObservations.properties.temperature);

  $.ajax({
      url:closestObservations,
  }).done(function(weatherdata) {
      var closestTemperature = weatherdata.properties.temperature.value;
      console.log("Closest temperature = " + closestTemperature);
      console.log(weatherdata.properties.temperature.value);
  });
  
}
getWeatherData();

Hi @jkreski,

Glad you solved it.

Just to point out, when pasting your code into the text editor, if you select that code and click on the </> menu button, it will format your code. Basically it wraps the code in opening and closing ticks ```.

Secondly the jquery ajax call seems unnecessary. This should suffice.

async function getWeatherData() {
  const weatherApiUrl = 'https://api.weather.gov/points/44.630978,-86.072480';
  const weatherApiData = await fetchJson(weatherApiUrl)
  
  const observationStationsUrl = weatherApiData.properties.observationStations
  // fetch the observationStations array property
  const { observationStations } = await fetchJson(observationStationsUrl)
  
  // fetch the data from the url in the first index of observationStations array
  const firstStationData = await fetchJson(observationStations[0])
  closestStation = firstStationData.properties.stationIdentifier;
  console.log("Closest reporting station = " + closestStation);

  const closestObservations = "https://api.weather.gov/stations/" + closestStation + "/observations/latest";
  console.log("Closest reporting URL = " + closestObservations);

  // fetch weather data
  const weatherdata = await fetchJson(closestObservations);
  const closestTemperature = weatherdata.properties.temperature.value;
  console.log("Closest temperature = " + closestTemperature);
}