0

I have some question regarding variables and attributions using javascript. I started out my code by doing document.ready() and inside it making two jQuery ajax calls. Later on I decided I need to reuse that code and run those calls again. So I grabbed that code and placed it inside a function. It looks like this.

$(document)
.ready(function() {
    var _groupJson;
    var _ticketsJson;
    _groupJson = groupsAjaxCall(_groupJson);
    _ticketsJson = ticketsAjaxCall(_ticketsJson); //rest of code...

Why the _groupJson? It represents an array of data that is filled by the ajax call (it used to work back when the ajax calls were inside document.ready). I need to pass those variables has parameters to other functions, like a print function that shows that data properly formatted in the HTML The function ajaxCall looks a bit like this:

function groupsAjaxCall(groups){
$.ajax({
    url: '/api/groups',
    dataType: 'json',
    success: function(json) {
        // get the `groups` array
        groups = json;

        showinHTML(groups);
    },
    error: function() {...}
});
return groups; }

The thing is, later on in document.ready part I do some event handling, and need the content of those _ticketsJson in other functions. But the _ticketsJson that the other functions receive is 'undefined'.

So, clearly I'm confusing a lot of concepts as to what does a variable represents in Javascript and how can I do var thisVar = thatVar;

Thank you in advance

EDIT: I don't think this due to the delay of an AJAX call. Inside the something AjaxCall function I can refer to the variable passed as parameter and show that information, I'm even calling there some printing functions that are displaying properly. But outside the function the _groupsJson should be holding the same value... I think... but it's not.

Downgoat
  • 13,771
  • 5
  • 46
  • 69
Thaenor
  • 611
  • 1
  • 9
  • 28
  • 4
    Your problems almost certainly have nothing to do with the mechanics of variable assignment, and everything to do with the fact that ajax is **asynchronous**. – Pointy May 20 '15 at 14:15
  • Can you please post a fiddle? – bernland May 20 '15 at 14:16
  • or at least the definition of `ticketsAjaxCall` and how you are using `_ticketsJson` so that we can close this as a dupe. – Kevin B May 20 '15 at 14:17
  • @Pointy thanks for the reply. I see your point, the weird thing is, the print function is working just fine, and the information is shown in the HTML. But the pagination functions, for instance, aren't working. I pass the _groupJson has a parameter and do _groupJson.lenght() but it turns up an error "cannot find size of 'undefined'" – Thaenor May 20 '15 at 14:18
  • Definitely sounds like a timing issue, hence async ajax. – Kevin B May 20 '15 at 14:18
  • @bernland I would if I could... but the ajax calls are made to a localhost Laravel server I made... in JsFiddle the ajax will never work... – Thaenor May 20 '15 at 14:19
  • 1
    `function groupsAjaxCall(groups){ return groups; }` You don't return the results of your ajax call... you return `groups`, which is the argument passed to your function, making the ajax call pointless. – Jeremy Thille May 20 '15 at 14:21
  • @Thaenor: What if you replace the ajax calls by simple variable assignments? Also, have you looked into https://developer.mozilla.org/en-US/docs/Web/Events/readystatechange – bernland May 20 '15 at 14:23
  • One quick side question (and please give a minute to proccess). would it be bad practice to make my ajax synchronous instead? I'm not entirely sure if that would solve my problem here but adding a loading screen and making the calls would probably help with the overall page design. As it is I'm showing a pretty page but with zero content that pops up a split second later... – Thaenor May 20 '15 at 14:26
  • 1
    @Thaenor: Yes, making AJAX calls synchronous would be very bad practice. It sounds like the problem you're facing is that the code invoking the asynchronous operations is expecting a return value. The very nature of asynchronous operations is that they don't return their value, they return immediately and later invoke a callback with the value. You need to structure your code to respond to the callback. – David May 20 '15 at 14:28
  • @Thaenor: Regarding your recent question edit... Where do you ever actually *set a value* to that variable? You initialize `_groupJson` as `undefined`, pass it to a function, return it from that function, and verify that it's still `undefined`. Nowhere do you actually set a value to it. (Except in the `success` callback, which is explained in greater detail in the linked question of which this is very likely a duplicate.) – David May 20 '15 at 14:37
  • @David a quick side question that would definitely solve my problem would be "How can I make the code for an ajax call reusable."... this all started because I assumed moving it into it's own separate function would solve the problem – Thaenor May 20 '15 at 14:40

2 Answers2

1

Making it a constructor

I would return an object with a onready function, this has a lot nicer syntax in my opinion:

function groupsAjaxCall(groups) {
    this.onready = function () {}; // Our onready function
    this.response = {}; // The response Variable
    var self = this; // "References" this scope and all the "this" variables

     $.ajax({
        url: '/api/groups',
        dataType: 'json',
        success: function(json) {
            self.response = json; // Sets the response
            self.onready.apply(self); // Calls the callback
            showinHTML(groups);
        },
        error: function() {/* ... */}
    });

}

Then someone can use this by calling:

var myCall = new groupsAjaxCall(groupsVariable);
//            ^^  Notice the new

myCall.onready = function () {
    console.log(this.response); //Do something with this.response
};

Adding a callback:

function groupsAjaxCall(groups, callback) {
     $.ajax({
        url: '/api/groups',
        dataType: 'json',
        success: function(json) {
            callback.apply(this,arguments);
//passes all arguments to callback   ^^
            showinHTML(groups);
        },
        error: function() {/* ... */}
    });

}

groupsAjaxCall(groupsVariable, function (response) {
    console.log(response); // Do something with response
});

The main reason you shouldn't run synchronous call is because XHRs take time to send and come back. In that time, it would freeze JavaScript. JavaScript runs on the UI thread meaning your page would also start to "freeze / get very slow".

Downgoat
  • 13,771
  • 5
  • 46
  • 69
  • Thank you for the answer. As a quick follow up question. Does this mean you would call something along the lines of myCall.onready = function () { //Do event handling for things that depend on the ajax data. }; Also, I'm going to need to access this in a lot of different functions. Like a search function. Is there a way I can store the array of data returned by the ajax call in a global variable, for example? – Thaenor May 20 '15 at 15:40
  • @Thaenor you can, in the onready add `window.response = this.response` that wil make it a global. You need yo make sure the AJAX has finished though. – Downgoat May 20 '15 at 22:25
  • Thanks for clarifying. I got things working now. And reusing the ajax functions. I even made the api more versatile so she can receive parameters or just return the default values. So I can call api/groups or api/groups/1 (for example) – Thaenor May 21 '15 at 11:16
  • this doesn't work. Declaring the ajax object makes the call, but the onReady callback runs right afterwards, I assumed it would wait for the ajax call to be completed. – Thaenor Jun 05 '15 at 08:44
  • @Thaenor yeah, it should waiter success to finish, then it will run – Downgoat Jun 05 '15 at 14:14
  • that's not what's happening on my case. – Thaenor Jun 05 '15 at 14:21
  • @Thaenor perhaps you may need to remove the line that says : `this.onready = function () {}` – Downgoat Jun 05 '15 at 14:41
  • So wait... if I don't do that then how do I call the callback in the main part it's getting called? – Thaenor Jun 05 '15 at 16:27
  • @Thaenor `this` is an object so when you assign it to the instance, it gets assigned to the variable – Downgoat Jun 05 '15 at 16:28
  • so... like this? http://pastebin.com/WePhzbHq @vihan1086? – Thaenor Jun 05 '15 at 16:35
  • Solved using this: http://stackoverflow.com/a/14754681/4631705 – Thaenor Jun 05 '15 at 16:49
1

In response to your comment:

a quick side question that would definitely solve my problem would be "How can I make the code for an ajax call reusable."... this all started because I assumed moving it into it's own separate function would solve the problem

Reusability works the same way with AJAX code as it does with any other code. However, you need to understand how that AJAX code works. Currently you're doing this:

function doSomething(someValue) {
    $.ajax({
        success: function (response) {
            someValue = response;
        }
    });
    return someValue;
}

This is inherently incorrect, as explained in great detail here. But this doesn't mean you can't re-use your AJAX code. It just means that you can't expect your AJAX functions to return a value, since AJAX by design doesn't do that.

Notice how the $.ajax() function itself works. It doesn't return a value, it expects you to supply it with a callback function. All you need to do is continue that pattern. Provide a callback function:

function doSomething(callback) {
    $.ajax({
        success: callback
    });
}

Then you can invoke your function with the callback:

doSomething(function (response) {
    // do something with the response
});

The same pattern holds... When using asynchronous operations, don't expect a return value. Instead, respond to the callback.

Community
  • 1
  • 1
David
  • 208,112
  • 36
  • 198
  • 279
  • if I had more reputation I would upvote this answer, because it really explains a lot of concepts that were actually confusing me. Thank you very much for taking the time to explain :) – Thaenor May 20 '15 at 16:11