Carlo
Carlo

Reputation: 4158

AJAX Jquery: execution order of events

I have a webpage with different elements (a list of links and two select boxes) connected between them. Clicking on them may affect one of the other element and all of their values contribuite to update a value to show on the page.

So, the code is this:

$(document).ready(function() {

    var someVar = '';

    $("select#size").bind('change', function() {
        someVar = $(this).val();
        console.log('first');
    });

    my_change();
    console.log('second' + someVar);
});

function my_change() {

    $.getJSON("photos/change_product", {json_stuff}, function(data) {
        var options = [];
        for (var i = 0; i < data.length; i++)  {
            options.push('<option value="' + data[i].id + '">' + data[i].label + '</option>');
        }
        $("select#size").trigger('change');
        $("select#options").html(options.join('')).trigger('change');
    })
};

};

When I load the page the my_change function is called. It does some stuff and then triggers a change event on a select-box. I need to update a value using what's inside this select box and only then let the execution to proceed. So what I need this code to do would be to print 'first', and then 'second' with the value of the variable. What actually happen is that it prints 'second' 'first'.

I think it's because I'm doing asynchronous calls. What can I do?

Upvotes: 0

Views: 2020

Answers (3)

aziz punjani
aziz punjani

Reputation: 25786

There's several ways to do this.

You could use jQuery $.when and call the console.log after the ajax response finishes.

$(document).ready(function() {

    var someVar = '';

    $("select#size").bind('change', function() {
        someVar = $(this).val();
        console.log('first');
    });

    $.when( my_change() ).then(function(){ 
            console.log('second' + someVar);
    }); 
});

function my_change() {

    return $.getJSON("photos/change_product", {json_stuff}, function(data) {
        var options = [];
        for (var i = 0; i < data.length; i++)  {
            options.push('<option value="' + data[i].id + '">' + data[i].label + '</option>');
        }
        $("select#size").trigger('change');
        $("select#options").html(options.join('')).trigger('change');
    })
};

};

Or you could add a callback argument to the my_change(callback) function.

$(document).ready(function() {

    var someVar = '';

    $("select#size").bind('change', function() {
        someVar = $(this).val();
        console.log('first');
    });

    my_change(function(){ console.log('second' + someVar) } ); 

});

function my_change(callback) {

    return $.getJSON("photos/change_product", {json_stuff}, function(data) {
        var options = [];
        for (var i = 0; i < data.length; i++)  {
            options.push('<option value="' + data[i].id + '">' + data[i].label + '</option>');
        }
        $("select#size").trigger('change');
        if( typeof callback !== 'undefined' && typeof callback === 'function' )
            callback(); 
        $("select#options").html(options.join('')).trigger('change');
    })
};

};

Upvotes: 2

Jasper
Jasper

Reputation: 76003

The 'second' console.log() is being called first since the asynchronous $.getJSON() call waits for the response from the server before firing its callback function. You could save the jqXHR object to a variable and then use that to run your 'second' consone.log() with $.when():

$(function() {

    var someVar = '';

    $("#size").on('change', function() {//on() is the same as bind() here
        someVar = $(this).val();
        console.log('first');
    });

    //save the jQuery XHR object from your $.getJSON request
    var jqXHR = my_change();

    //when the above jQuery XHR object resolves, it will fire the second console.log
    $.when(jqXHR).then(function () {
        console.log('second' + someVar);
    });
});

function my_change() {

    //here we return the jQuery XHR object for the $.getJSON request so we can run code once it resolves
    return $.getJSON("photos/change_product", {json_stuff}, function(data) {
        var options = [];
        for (var i = 0; i < data.length; i++)  {
            options.push('<option value="' + data[i].id + '">' + data[i].label + '</option>');
        }
        $("#size").trigger('change');
        $("#options").html(options.join('')).trigger('change');
    })
};

Here is documentation for $.when(): http://api.jquery.com/jquery.when

A quick side-note: it is generally slower to add a tag-type to a selector, especially when you are selecting IDs as that is already a very fast method of selecting elements.

Upvotes: 1

RightSaidFred
RightSaidFred

Reputation: 11327

Any code that relies on the response of the getJSON must be placed in, or called from, the getJSON callback.

That's what a callback is for.

You should note that your my_change function will not have access to the someVar variable because it is local to the ready() callback.

To remedy this, move the my_change function inside the ready() callback.

Or just pass a function directly to my_change.

my_change(function() {
    console.log('second' + someVar);
});

And have the getJSON callback invoke the function.

function my_change( func ) {

    $.getJSON("photos/change_product", {json_stuff}, function(data) {
        var options = [];
        for (var i = 0; i < data.length; i++)  {
            options.push('<option value="' + data[i].id + '">' + data[i].label + '</option>');
        }
        $("select#size").trigger('change');
        $("select#options").html(options.join('')).trigger('change');

        func();
    });
}

Upvotes: 0

Related Questions