2

My problem is a bit hard to explain, but I'll try my best. I have 3 tabs, one of which is active by default. Every time a tab is activated, an event fires. However, the tab headings are set in a function set earlier. My code is given below:

<script>
    var queues = {};

    $(document).ready(function(){

        getQueueData();
        $('.nav a[href="#queue1"]').tab('show');
    });

    function getQueueData(){

        $.post('interface/getqueues.php', function(data){


            var str = "#q";
            for (var i=0; i<data.data.length; i++){
                str += i;
                $(str).text(data.data[i]["name"]);
                queues[str] = data.data[i];
                console.log(queues[str]);
                str = "#q";   
            }
        });
    }

    $('a[data-toggle="tab"]').on("shown.bs.tab", function(event){
        console.log("HELLO WORLD!");
        var elem = $(this).attr("id");
        var id = "#"+elem;
        console.log(queues);
    });


</script>

Now, the getQueueData() function should execute BEFORE the shown.bs.tab event is fired. This is necessary for the latter event to be able to access the values in the queues object. In fact, however, when the page is loading, the shown.bs.tab function is executing first, followed by getQueueData(). This means that in the shown.bs.tab method, queues is undefined the first time. I know it because the HELLO WORLD and an empty object is printing in the log before the console.log(queues[str]) output. Does anyone know how I can make the getQueueData() execute completely before going to the 'shown.bs.tab' event?

user3033194
  • 1,775
  • 7
  • 42
  • 63
  • 1
    It sounds like you need to use a `promise`. Check out this: http://stackoverflow.com/questions/20122227/jquery-deferred-and-promise-for-sequential-execution-of-synchronous-and-asynchro – scgough Aug 21 '15 at 08:59
  • I believe you need to fire the `shown.bs.tab` event after you finish the async post, so call it at the end of the `$.post`s callback function. – Glen Keane Aug 21 '15 at 09:00
  • 1
    Or just simply wrap your `$('a').on('shown.bs.tab')` in a function wrapper and call it from the successful post callback. – somethinghere Aug 21 '15 at 09:00

2 Answers2

4

Haha!

Pretty common question for beginners in javascript's asynchronous realm. :) Welcome to the jungle mate. ;)

Here is the magic trick. Move your $('.nav a[href="#queue1"]').tab('show'); inside $.post function. Put it after your for loop. :)

function getQueueData(){

        $.post('interface/getqueues.php', function(data){


            var str = "#q";
            for (var i=0; i<data.data.length; i++){
                str += i;
                $(str).text(data.data[i]["name"]);
                queues[str] = data.data[i];
                console.log(queues[str]);
                str = "#q";   
            }
            $('.nav a[href="#queue1"]').tab('show'); // << Triggre your show event here.
        });
    }

And everything will work great.

Will be updating this answer with explanation, soon. :)

==

EDIT:

Explanation.

Your concern, that getQueueData() should execute BEFORE shown.bs.tab is already fulfilled. However, it does appear that getQueueData() gets executed after your tabs show event. Why is that? Because you have an asynchronous call inside your getQueueData(). The script will make an API call to interface/getqueues.php and because it is asynchronous, it's heroic nature takes over and it doesn't wait until the server responds. It continues it's execution and returns from function getQueueData() and goes ahead and triggers the show event on your tabs (because the next statement after a call to getQueueData is your tabs's show statement.

When the server responds, it will trigger the callback you have specified in your $.post function call. Therefore when this callback executes (which is after response is received from the server, the show event on your tabs has already taken place. And this is why it seems like getQueueData is getting executed after tabs show event. But in reality, it is executed before the tabs show event and the call back of $.post happens whenever it's necessary.

For more information, please read this awesome guide.

AdityaParab
  • 7,024
  • 3
  • 27
  • 40
  • Thank you, it worked! I am still trying to fully understand these asynchronous operations. – user3033194 Aug 21 '15 at 09:08
  • Once I did put it in the getQueueData() method, but after the $.post function. It was showing undefined. – user3033194 Aug 21 '15 at 09:11
  • @user3033194: Check the updated answer, let me know in case of any questions. – AdityaParab Aug 21 '15 at 09:13
  • I see, so the call order is - first getQueueData() in which an asynchronous call is made, then the tabs function, then back to the callback function code for $.post in getQueueData() (when the async call is completed). Is this right? – user3033194 Aug 21 '15 at 09:21
  • OK, I got it now. Thank you for the explanation. – user3033194 Aug 21 '15 at 09:24
  • True. :) Play around with it a little bit and you'll soon start loving this bavaviour, much more than traditional, straight forward programming. :) – AdityaParab Aug 21 '15 at 09:31
2

post is an asynchronous method. It means that its result is not necessarily get just after it is called. Javascript is such language that it won't wait for the result of an async method to continue to execute the rest of code.

Thus, you have to synchronizde your code by your own.

One thing that you can use for this purpose is deferred object. When you create it, it has a pending state. Whenever you resolve or reject over that object, you will get notified.

Below, notice how you could use it.

  • You can wait for a function to be finished with a deferred object.

.

<script>
var queues = {};

var deferred = $.Deferred();

$(document).ready(function(){

    getQueueData();
    deferred.then(function() {
        $('.nav a[href="#queue1"]').tab('show');
    });

});

function getQueueData(){

    $.post('interface/getqueues.php', function(data){


        var str = "#q";
        for (var i=0; i<data.data.length; i++){
            str += i;
            $(str).text(data.data[i]["name"]);
            queues[str] = data.data[i];
            console.log(queues[str]);
            str = "#q";   
        }

        deferred.resolve();

    });
}

Mert Mertce
  • 1,614
  • 3
  • 21
  • 32