FCC Weather App Example: Scope questions

FCC Weather App Example: Scope questions
0

#1

Hi FCC, this is my first post! :slight_smile:

While looking through the code for FCC weather app (I know, I’m not supposed to look), I hit something in the Javascript that I can’t seem to figure out. I think it’s a question regarding scope??

In the top of the script, global variable currentTempInCelsius is defined.

var currentTempInCelsius;

Later, this variable is assigned the temperature value from the API response within the function getWeather (with some math rounding applied). And is then immediately sent to #temp to be displayed in the html.

function getWeather(lat, lon) {
  var urlString = api + lat + "&" + lon;
  $.ajax({
    url: urlString, success: function (result) {
  $("#city").text(result.name + ", ");
  $("#country").text(result.sys.country);
  currentTempInCelsius = Math.round(result.main.temp * 10) / 10;
  $("#temp").text(currentTempInCelsius + " " + String.fromCharCode(176));
  $("#tempunit").text(tempUnit);
  $("#desc").text(result.weather[0].main);
  IconGen(result.weather[0].main);
    }
  });
}

So far this makes complete sense to me. The API is called, the response comes, the temperature gets assigned to the variable. The value of the variable gets put in the #temp text and it displays on the site. Success!

What I don’t understand:

In another area, there is a separate function used to convert Celsius to Fahrenheit. In the last line it also uses currentTempInCelsius to successfully adjust the #temp text.

$("#tempunit").click(function () {
    var currentTempUnit = $("#tempunit").text();
    var newTempUnit = currentTempUnit == "C" ? "F" : "C";
    $("#tempunit").text(newTempUnit);
  
    if (newTempUnit == "F") {
      var fahTemp = Math.round(parseInt($("#temp").text()) * 9 / 5 + 32);
      $("#temp").text(fahTemp + " " + String.fromCharCode(176));
    } else {
      $("#temp").text(currentTempInCelsius + " " + String.fromCharCode(176));
}

but I’m not sure how it has access to the correct value of currentTempInCelsius.

I tested a few console.logs throughout various areas of the script to see what it would give me for currentTempInCelsius:

40 PM

Anywhere in the global space, it is still undefined. But within several different, unrelated, functions, the value is there!

My question: What has access to currentTempInCelsius? And where/how is that determined in this script?

I’m not sure if this is an API thing or what. I would have thought anything assigned to currentTempInCelsius would be accessible anywhere since it is a global variable.

(I hope all this makes sense! TIA)


#2

Hey. I’m not sure where you put your console.log statements but that variable will be undefined until after the asynchronous call to the API completes.
At that point, it should be defined no matter where in the code it’s accessed from.

Is it possible that you put your log lines in places that would execute before the API call returned?


#3

What @Nektario said is correct.

By defining it at the top of his script, the variable is accessible everywhere but the only place where it’s actually assigned is inside the success function of the Ajax call. Without that, it would still be undefined.

You can check this by deleting line 37.

(Also, I don’t see any problem at looking at the code, as long as you can learn from it).


#4

To expand on what @Nektario said above, here are some various points in the code and the corresponding value of currentTempInCelsius:

  1. At first currentTempInCelsius is declared and has a value of undefined.

  2. Once the DOM has loaded, and the the anonymous function in the navigator.geolocation.getCurrentPosition api call successfully returns some data, the getWeather function is called. At this point currentTempInCelsius is still undefined.

  3. Inside the getWeather function, the $.ajax function attempts to get weather data back from the FCC weather api. If it is successful, then we eventually get to the following line:

currentTempInCelsius = Math.round(result.main.temp * 10) / 10;

Now, currentTempInCelcius has an actual value and can be used in code after this line in the function. Also, since it was globally declared, the click event handler for the tempunit element can have access to it also.


#5

Thanks everyone! It’s nice to know that it does work the way I would have believed it to work, by becoming globally accessible. That’s definitely more useful.

Still trying to understand some things. I just played around with it some more to try some precise spots for “console.log(currentTempInCelsius);”
–I put it at the very end of the code, it still read undefined (long after the API was successfully called).
–I tried putting it on line12, right after the getWeather function (which contains the $.ajax function, so should be after successful API call)

Both returned undefined. Is this something to do with hoisting? Is console.log triggered before the functions?


#6

The Ajax call is asynchronous, meaning that the javascript execution moves to the next line as soon as the request is sent; it doesn’t wait for a response.

Try doing this, change line 37 from:

currentTempInCelsius = Math.round(result.main.temp * 10) / 10;

to:

setTimeout(function() {
 currentTempInCelsius = Math.round(result.main.temp * 10) / 10;
}, 500);

You will see that the temperature will be undefined at the beginning but after switching to fahrenheit and back to celsius (in other words, after the else statement at line 25 that calls currentTempInCelsius again) it will have the correct value. That’s because the value is still being set but, by that time, javascript is already at the next line, the one assigning the value to #temp:

$("#temp").text(currentTempInCelsius + " " + String.fromCharCode(176));


#7

@noyb beat me to the answer, but I will go ahead and post what I was working on.

What you are not understanding is the asynchronous nature of the $.ajax call.

Check out this JSFiddle link where I only make the $.ajax call on some known coordinates. You will notice that appendTo on the line after the $.ajax call runs before the appendTo inside the $.ajax call. The code on the outside keeps running even though the results have not gotten back from the $.ajax call. Once the data is successfully received, the function inside runs which displays an actual temperature value.

https://jsfiddle.net/urxov9np/1/

Code in the JSFiddle is:

var currentTempInCelsius;
var urlString = "https://fcc-weather-api.glitch.me/api/current?lat=35&lon=139";
$.ajax({
  url: urlString, success: function(result) {
    currentTempInCelsius = Math.round(result.main.temp * 10) / 10;
    $("<p>inside $.ajax call currentTempInCelsius is " + currentTempInCelsius + "</p>").appendTo(document.body);
  }
});
$("<p>right after $.ajax call currentTempInCelsius is " + currentTempInCelsius + "</p>").appendTo(document.body);

#8

Asynchronous! Well that makes obvious sense now!

Thank you so much noyb and rmdawson71 for patiently talking me through that until it clicked :blush: