0

I'm currently working on a script that will check all the the links of a page and highlight any broken links. Synchronously, this wasn't a major task, but I only just started working with Asynch, so any help with my code would be much appreciated.

var size = 0;
var itemsProcessed = 0;
    console.log("Size: " + size);

function linkCheck() {
var body = document.querySelectorAll("*");
var checker = Array.from($(body).filter("a"));
size = (Object.keys(checker).length);

    console.log("Size: " + size);
var list;

checker.forEach(function (anchor) {
    var link = $(anchor).attr("href");
    itemsProcessed++;
    console.log("Items: " + itemsProcessed);
    if (itemsProcessed == size) {
        colorUp(list);
    }
    var req = new XMLHttpRequest();
    req.open('HEAD', $(checker).attr("href"), true);
    itemsProcessed++;
    try {
        req.send(null);
    } catch (e) {
        console.log("ERROR");
        return true;
    }
    console.log(req.status);
    var data = (req.status !== 200);
    if (data) {
        //console.log("Added to List")
            list += data;
    }
    console.log(link, data);
});
}

function colorUp(list) {
console.log("DONE");
$(list).css({
    "border": "3px solid #ffb700",
    "background": "repeating-linear-gradient(135deg, #FFE0B2, #FFE0B2 5px, #ffffff 5px, #ffffff 10px)"
});
}


linkCheck();

Currently, I'm having trouble waiting for the forEach to finish, but if you have any suggestions, or anything I can do to make the problem clearer, feel free to leave a post!

Thank you guys

Tân
  • 1
  • 15
  • 56
  • 102
Gwinert
  • 67
  • 1
  • 7
  • Use `document.querySelectorAll('a');' or `$('a')` to get all the anchors. Also decide if you're going to use jQuery or not. If you are, use it for everything and you'll be a lot happier. Note also that ajax (`XMLHttpRequuest`) is asynchronous, so `req.status` will not necessarily be correct when you are checking it. – Heretic Monkey Feb 21 '17 at 18:21
  • Just because I'm unfamiliar with this, How can I make it wait till req.status is loaded before I check it? Also, how would I make the forEach portion asynchronous before I call colorUp()? Thank you for responding Mike. – Gwinert Feb 21 '17 at 18:34
  • 1
    You'll want to do some research into Promises and a function named `Promise.all`. If you use jQuery's `$.ajax` method, it's a bit easier. Otherwise, you'll have to make `XMLHttpRequest` work with promises, which is no small undertaking. You could probably find a library that does it for you though. Basically, research, research, research :). – Heretic Monkey Feb 21 '17 at 18:36
  • Possible duplicate of [ES6 Promises - something like async.each?](http://stackoverflow.com/questions/32028552/es6-promises-something-like-async-each) – jib Feb 21 '17 at 22:44

2 Answers2

0

If you need to wait for a number of requests to run one after another and do something after they finish you should use a reduce Array method.

checker.reduce((promise, anchor) => {
    return promise.then(() => $.ajax({type: "HEAD", url: $(anchor).attr("href")}));
}, $.Deferred().resolve()).then(responseFromLastRequest => {
    // Do your stuff here     
});

Or, if you don't care about the order of requests, you can run them simultaneously:

$.when.apply($, checker.map(anchor => {
    return $.ajax({type: "HEAD", url: $(anchor).attr("href")});
})).then(() => {
    // Do your stuff here
});
0

As was said, working with XMLHttpRequest is not fun. Here is a complete example taking advantage of jQuery. While doing research about Promises is certainly a good idea, seeing it at work first might be better for a beginner.

function linkCheck() {
    var itemsProcessed = 0;

    var $checker = $('a');
    var size = $checker.length;
    console.log("Size: " + size);

    var $list = $();

    $checker.each(function (anchor) {
        var link = $(anchor).attr("href");
        itemsProcessed++;
        $.ajax(link, {
            type: 'HEAD',
            success: function (response, status, xhr) {
                var data = (req.status !== 200);
                if (data) {
                    $list.pushStack([anchor]);
                }
                console.log(link, data);
            },
            error: function (xhr, err) {
                console.log("ERROR");
            },
            complete: function () {
                console.log(req.status);
                console.log("Items: " + itemsProcessed);
                if (itemsProcessed == size) {
                    colorUp();
                }
            }
        });
    });
}

function colorUp() {

    console.log("DONE");
    $(list).css({
        "border": "3px solid #ffb700",
        "background": "repeating-linear-gradient(135deg, #FFE0B2, #FFE0B2 5px, #ffffff 5px, #ffffff 10px)"
    });
}


linkCheck();

The $.ajax(url, settings) function takes as the second parameter an object that steers the 'waiting for a response' process. It defines a bunch of functions that are to be executed asynchronously at a later point in time, after the linkCheck() function has finished.

After a response from the server arrives, either the function listed for the success or the one listed for the error property is executed, and in both cases, after that the complete function gets executed.

The main point to remember is: if you define any sort of send function, the original function proceeds to execute without waiting for the answer. If you want to do something when the response arives, you have to define it in a closure.

ccprog
  • 20,308
  • 4
  • 27
  • 44