LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   [JS Newbie] Having a hard time understanding a variable assignment issue (https://www.linuxquestions.org/questions/programming-9/%5Bjs-newbie%5D-having-a-hard-time-understanding-a-variable-assignment-issue-4175560534/)

lmcilwain 12-03-2015 10:45 AM

[JS Newbie] Having a hard time understanding a variable assignment issue
 
Hello all,

I have a feeling this is a super simple issue but I can't seem to figure out how to resolve it. I am attempting to write a JS method (using CoffeeScript) that calls data from a public API. I am able to retrieve the data but I am not able to assign it to a variable outside of the method for use.

In summary of my issue: after calling getWeather(city), this.city is still set to undefined

Any help would be greatly appreciated:

Here is the current code I am working with:
CoffeeScript:

Code:

  this.city = undefined
  this.getData = (city)->
    weather = getWeather(city)
    weather.done (result)->
      this.city = result
    .fail ->
      console.log(error)
      alert(error)
  undefined

getWeather = (city) ->
  promise = $.Deferred()
  $.ajax
    url: "http://api.openweathermap.org/data/2.5/weather"
    dataType: 'json'
    data:
      q: city
      APPID: "someAPIkey"
    success: (result) ->
      promise.resolve(result.main)
    error: ->
      promise.reject("Invalid Location")
  return promise

Which generates the following
Code:

  this.city = void 0;
  this.getData = function(city) {
      var weather;
      weather = getWeather(city);
      return weather.done(function(result) {
        return this.city = result;
      }).fail(function() {
        console.log(error);
        return alert(error);
      });
    };

 getWeather = function(city) {
    var promise;
    promise = $.Deferred();
    $.ajax({
      url: "http://api.openweathermap.org/data/2.5/weather",
      dataType: 'json',
      data: {
        q: city,
        APPID: "someAPIkey"
      },
      success: function(result) {
        return promise.resolve(result.main);
      },
      error: function() {
        return promise.reject("Invalid Location");
      }
    });
    return promise;
  };


Guttorm 12-03-2015 11:09 AM

Hi

I'm not sure I understand completely, and don't know CoffeeScript. But the success and error callbacks that are sent to the ajax call shouldn't return anything. If they do, the return value is not assigned to anything. Also, ajax calls are asyncronous by default. That means the getWeather function returns first, the success or error functions are called later when the client get response from the server.

If you add "async:false" to the ajax call parameters, it doesn't return until you get data from the server. Then you can assign the result to a variable that you define in the getWeather function. And this variable you can return. But it's not a good solution. Nothing else can happen until you get response from the server, and that can take a long time. A better solution is simply to do whatever you need to do inside the success function.

lmcilwain 12-03-2015 11:24 AM

I was under the impression that .done() waits for $.ajax() to finish before executing its line of code. The .done() method associates with the promise.success() method and the .fail() method associates with the promise.error() method.

Maybe its time to scrap this code and start over and see what I am misunderstanding :(

grail 12-03-2015 12:28 PM

I am not all that familiar with js and only have learned a little coffee scripting (from rails), but even without any real understanding the script looks flawed.

If you look at the generated code, you set 'this.city' at the start, but the next time it 'looks' like it is getting set is inside a return value of a function. Thinking of most normal scopes,
if you are setting the value inside a function as a return value then it is never actually being assigned to your outer scope copy of the value.
Another weird part is in the empty function where you pass in 'result', you actually set your value and the returned item to the passed in item ... this also seems a little counter intuitive.

The above may be all wrong but it is just what it looks like to me ;)

lmcilwain 12-03-2015 04:42 PM

Thanks for the help guys. I actually ended up having to go back to my code school course. They way they instructed was the following (lines in bold):

Code:

  this.city = void 0;
  some_var = this
  this.getData = function(city) {
      var weather;
      weather = getWeather(city);
      return weather.done(function(result) {
        return some_var.city = result;
      }).fail(function() {
        console.log(error);
        return alert(error);
      });
    };

 getWeather = function(city) {
    var promise;
    promise = $.Deferred();
    $.ajax({
      url: "http://api.openweathermap.org/data/2.5/weather",
      dataType: 'json',
      data: {
        q: city,
        APPID: "someAPIkey"
      },
      success: function(result) {
        return promise.resolve(result.main);
      },
      error: function() {
        return promise.reject("Invalid Location");
      }
    });
    return promise;
  };

I am planning to dig deeper into why I had to do it this way but my first guess would be that I am not understand stopes well so I should brush up on that.

firstfire 12-08-2015 08:01 AM

Hi.

Variable this in the following code
Code:

weather.done(function(result) {
  return this.city = result;
})

refers to the context of innermost anonymous function, not the context of its (grand-)parent function (where this.city variable was set). If you want to access parent context you either save it into a variable (like you did in your last post):
Code:

var self = this;
...
weather.done(function(result) {
  self.city = result;
});

or set the context of the inner function explicitly using bind (you'll have to do this twice because you have two-level function nesting). Most return statements in your code can be removed.

Anyway, the problem with such code is that you never know if the variable have meaningful value or not. If your code depend on correct value of the variable, you should either use callbacks (say, put your logic directly into success callback of ajax) or use promises. I will not try to review your code from the promise point of view, because the way you use them seems pointless to me -- just set the variable in success callback. Btw, I've heard jQuery promises are flawed, so avoid them if you can :) I prefer Q for promises, but there are lots of alternatives available. Nevertheless Q's documentation is a good starting point to learn about promises.

Good luck!

Guttorm 12-10-2015 11:03 AM

Hi

When coding asyncronous stuff, hiding the complicated stuff in functions can just make problems. And it's not at all easier to read or understand. I think something like this is more clear:

Code:

<input type="text" id="city">
<script>

$("#city").on("change",function() {
    $.ajax({
      url: "http://api.openweathermap.org/data/2.5/weather",
      dataType: 'json',
      data: {
        q: $("#city").val(),
        APPID: "someAPIkey"
      },
      success: function(result) {
        // update the weather on the page. Put some text in a div or something?
        // you could add a function to parse and format the result, but why not put that code here?
      },
      error: function() {
        // display some error message or maybe do nothing?
      }
    });
});
</script>



All times are GMT -5. The time now is 02:26 AM.