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) {});
        }
    }
5
  • call ajax get and post method as async:false Commented Oct 9, 2014 at 5:55
  • stackoverflow.com/questions/5000415/… Commented Oct 9, 2014 at 6:03
  • @abc123 No, no, no! Do not use async: false, that is the worst possible advice in this scenario! Commented Oct 9, 2014 at 7: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. Commented Oct 9, 2014 at 7:14
  • @abc123 I've provided an answer below. Commented Oct 9, 2014 at 7:14

2 Answers 2

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()

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this. If you ever need a Oneplus One invite; PM @kallen on the Oneplus forums.
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) {});
        }
    }

3 Comments

this is (IMHO) the naive pre-jQuery 1.5 method. Just use $.when instead!
Well explained, though, Mark. +1 from me.
-1 from me, because it (unnecessarily) tightly couples the call to likeLinks inside the getLikeURLs() function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.