0

I need a little help. I'm trying to run my second function "likeLinks();" but only after my first function "getLikeURLs();" is finished. This is because my 2nd function relies on the links Array to execute. It seems like they are trying to run at the same time.

Any help would be appreciated.

    var links = [];
    var url = '/' + window.location.pathname.split('/')[1] + '/' + window.location.pathname.split('/')[2] + '/'
    getLikeURLs();
    likeLinks();

    function getLikeURLs() {
        for (i = 1; i < parseInt(document.getElementsByClassName('PageNav')[0].getAttribute('data-last')) + 2; i++) {
            var link = $.get(url + 'page-' + i, function(data) {
                //gets the like links from current page
                $(data).find('a[class="LikeLink item control like"]').each(function() {
                    links.push($(this).attr('href')); // Puts the links in the Array
                });
            });
        }
    }

    function likeLinks() {
        for (t = 0; t <= links.length; t++) {
            var token = document.getElementsByName('_xfToken')[0].getAttribute('value')
            $.post(links[t], {
                _xfToken: token,
                _xfNoRedirect: 1,
                _xfResponseType: 'json'
            }, function(data) {});
        }
    }
  • call ajax get and post method as async:false – abc123 Oct 09 '14 at 05:55
  • http://stackoverflow.com/questions/5000415/javascript-jquery-call-a-function-after-previous-function-is-complete – khizerbajwa Oct 09 '14 at 06:03
  • @abc123 No, no, no! Do _not_ use `async: false`, that is the worst possible advice in this scenario! – Alnitak Oct 09 '14 at 07:04
  • @Alnitak i had similar situations where i used this async: false,i know there is performance issues and i wanted to change it with better possible substitute. If you can, please suggest any possible substitute for this scenario. That will be a great help. – abc123 Oct 09 '14 at 07:14
  • @abc123 I've provided an answer below. – Alnitak Oct 09 '14 at 07:14

2 Answers2

1

The link variables are actually jQuery deferred objects - store them in an array and then you can use $.when() to create a mew deferred object that only resolves when all of the previous $.get() operations have completed:

function getLikeURLs(url) {     // NB: parameter, not global
    var defs = [], links = [];  // NB: links no longer global

    for (...) {
        var link = $.get(...);
        defs.push(link);
    }

    // wait for previous `$.get` to finish, and when they have create a new
    // deferred object that will return the entire array of links
    return $.when.apply($, defs).then(function() { return links; });
}

Then, to start the chain of functions:

getLikeURLs(url).then(likeLinks);

Note that likeLinks will now be passed the array of links instead of accessing it from the global state. That function should also be rewritten to allow you to wait for its $.post calls to complete, too:

function likeLinks(links) {
    // loop invariant - take it outside the loop
    var token = document.getElementsByName('_xfToken')[0].getAttribute('value');

    // create array of deferreds, one for each link
    var defs = links.map(function(link) {
        return $.post(link, {
            _xfToken: token,
            _xfNoRedirect: 1,
            _xfResponseType: 'json'
        });
    });

    // and another for when they're all done
    return $.when.apply($, defs);
}

p.s. don't put that (relatively) expensive parseInt(document.getAttribute(...)) expression within the for statement - it'll cause it to be evaluated every iteration. Calculate it once outside the loop and store it in a variable. There's a few other places where you're repeating calls unnecessarily, e.g. window.location.pathname.split()

Alnitak
  • 334,560
  • 70
  • 407
  • 495
0

EDIT: My answer discusses the issue but see Alnitak answer for a much better solution.

The get in getLikeURLs and the put in likeLinks are both asynchronous. The calls to both of these function return immediately. When data is returned from the called server at some indeterminate time later, the callback functions are then called. The puts could return before the gets which would be a problem in your case. Also note that JavaScript is NOT multi-threaded so the two methods, getLikeURLs and likeLinks will never run at the same time. The callback functions, on the other hand, might be called at anytime later with no guarantee as to the call back order. For example, the 3rd get/put might return before the 1st get/put in your loops.

You could use $.ajax to specify that the gets and puts are synchronous but this is ill advised because the browser will hang if ANY get/put doesn't return in a reasonable amount of time (e.g. server is offline). Plus you don't have the "multi-tasking" benefit of sending out a lot of requests and having the various servers working at the same time. They would do so serially.

The trick is to simply call likeLinks form the callback function in getLikeURL. Your case is a little tricky because of the for loop but this should work:

var links = [];
    var url = '/' + window.location.pathname.split('/')[1] + '/' + window.location.pathname.split('/')[2] + '/'
    getLikeURLs();
    //likeLinks();  // Don't call yet.  Wait for gets to all return.

    function getLikeURLs() {
        var returnCount = 0;   // Initialize a callback counter.
        var count = parseInt(document.getElementsByClassName('PageNav')[0].getAttribute('data-last')) + 1;
        for (i = 0; i < count; i++) {
            var link = $.get(url + 'page-' + (i + 1), function(data) {
                //gets the like links from current page
                $(data).find('a[class="LikeLink item control like"]').each(function() {
                    links.push($(this).attr('href')); // Puts the links in the Array
                });

                // If all gets have returned, call likeLinks.
                returnCount++;
                if (returnCount === count) {
                   likeLinks();
                }
            });
        }
    }

    function likeLinks() {
        for (t = 0; t <= links.length; t++) {
            var token = document.getElementsByName('_xfToken')[0].getAttribute('value')
            $.post(links[t], {
                _xfToken: token,
                _xfNoRedirect: 1,
                _xfResponseType: 'json'
            }, function(data) {});
        }
    }
moomoo
  • 826
  • 10
  • 9