简体   繁体   English

为什么空白警报语句会影响其他代码的执行?

[英]Why do blank alert statements affect execution of other code?

I'm working on the front page of a website that will have a section that will show news articles. 我正在网站的首页上工作,该网站的某个部分将显示新闻报道。 The articles will fade into the next one every 10 seconds. 每10秒钟,这些文章就会淡入下一个。 For some reason the code only executes correctly (keep in mind it's not entirely finished so there may be other errors) with the inclusion of a couple alert() statements. 由于某些原因,代码只正确执行(请注意,它还没有完全完成,因此可能会出现其他错误),其中包含几个alert()语句。 These were previously in there just for debugging, but currently, it seems as though they serve some functional purpose. 这些以前只是为了调试而存在的,但是目前看来,它们已达到某些功能目的。 Without them, the code will give different results if any at all. 没有它们,代码将给出不同的结果(如果有的话)。 I'm mainly a Java programmer, so there are probably some idiosyncrasies about JavaScript alert() statements that I'm not familiar with. 我主要是Java程序员,所以可能有一些我不熟悉的关于JavaScript alert()语句的特质。 The other odd thing that I noticed was that at times I would run the code multiple times with no changes and get different results. 我注意到的另一个奇怪的事情是,有时我会多次运行代码而没有任何更改,并且会得到不同的结果。 I used some of the alert() statements in the loadArticles() function to output the value of i and would occasionally get a different result without changing the code. 我在loadArticles()函数中使用了某些alert()语句来输出i的值,并且偶尔会得到不同的结果而无需更改代码。 The only idea I have so far is that my computer is taking time to run the statements that is allowing some other process to finish, but there shouldn't be any multi-threading involved. 到目前为止,我唯一的想法是我的计算机花时间运行允许其他进程完成的语句,但是不应涉及任何多线程。

The init() function is called in onload from the HTML and there's a div with id="news" somewhere in the center of the page. 在HTML的onload中调用了init()函数,并且在页面中央某处有一个id =“ news”的div。

On top of the main question, extra credit for anyone who could help out with why I'm sometimes not getting the articles to fade in and out. 最主要的问题是,任何可以帮助我为什么有时没有让文章淡出和淡出的人都可以得到额外的荣誉。 I'm pretty sure it has something to do with the article or container being null, but I haven't had time to get to that yet. 我很确定这与文章或容器为null有关,但是我还没有时间解决这个问题。

Here's the JavaScript: 这是JavaScript:

var article_count = 0;
var count = 0;

function init() {

    getArticleCount();
    loadArticles();
    changeSlide();

    resize();
    resize();

}

function getArticleCount() {

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {

        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {

            article_count = xmlhttp.responseText;

        }

    };

    xmlhttp.open("GET", "getArticleCount.php", true);
    xmlhttp.send();

}

function loadArticles() {
    alert();
    for(i = 1; i <= article_count; i++) {
        alert();
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {

            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                alert();
                var news = document.createElement("iframe");
                news.className = "news";
                news.src = "articles/" + xmlhttp.responseText;
                news.style.zIndex = 0 - i;

                var container = document.getElementById("news");
                container.appendChild(news);

            }

        };
        alert();
        xmlhttp.open("GET", "getArticles.php?q=" + i, true);
        xmlhttp.send();
        alert();

    }
}

function changeSlide() {

    var article = document.getElementsByClassName("news")[count];
    var interval = setTimeout(function() {

        var fadeOut = article.fadeOut(1000, function() {

            if(count < article_count) {

                count++;
                changeSlide();

            } else {

                count = 0;
                resetSlides();

            }

        });

    }, 10000);

}

function resetSlides() {

    var articles = document.getElementsByClassName("news");

    for(j = 0; j < article_count; j++) {

        var fadeIn = articles[j].fadeIn(1000);

    }

    changeSlide();

}

function resize() {

    var body = $(document.body);
    var news = $("#news");

    $("#menu_left").width((body.outerWidth() - news.outerWidth()) / 2 - 3);
    $("#menu_right").width((body.outerWidth() - news.outerWidth()) / 2 - 3);
    $("#menu_contact").width(body.outerWidth());

}

There are lots of mistakes in your code, mostly related to the asynchronous nature of Ajax calls. 您的代码中有很多错误,大多数与Ajax调用的异步特性有关。 You will need to more about programming with asynchronous operations to write correctly functioning and reliable and consistent code. 您将需要更多有关使用异步操作进行编程的知识,以编写功能正确且可靠且一致的代码。

alert() statement change the relative timing of asynchronous operations (such as Ajax calls vs. when your other code runs. alert()语句更改异步操作的相对时间(例如Ajax调用与运行其他代码的时间)。

In general, stop using alert() statements at all as a debugging tool because it can influence the timing too much. 通常,完全不要将alert()语句用作调试工具,因为它可能会严重影响时间。 Instead, use console.log() statements. 而是使用console.log()语句。 Since console.log() just outputs to the console and does not block execution of the Javascript thread at all, it will not impact the timing of things nearly as much as an alert() statement. 由于console.log()仅输出到控制台,并且根本不阻止Javascript线程的执行,因此它不会像alert()语句那样影响事物的时序。

Here's a trivial example to show you how an alert() can change the timing of things: 这是一个简单的示例,向您展示alert()如何改变事物的时间安排:

var img = new Image();
img.src = "http://somedomain.com/myimg.jpg";
alert("Press OK to continue");
if (img.complete) {
    console.log("image is done loading");
} else {
    console.log("image is not yet done loading");
}

With the alert statement, you will get image is done loading in the console. 使用alert语句,您将image is done loading在控制台中image is done loading Without the alert, you will get image is not yet done loading . 没有警报,您将获得image is not yet done loading The alert has changed the flow of your code. 该警报已更改您的代码流。


Another thing that can affect the timing of your code is whether resources are in the browser cache or must be loaded over the network. 可能影响代码时序的另一件事是资源是位于浏览器缓存中还是必须通过网络加载。 In nearly all cases, properly written code that only uses resources when it knows they have been loaded will continue to work in either case. 在几乎所有情况下,仅在知道已加载资源的情况下才使用资源的正确编写的代码将在两种情况下继续起作用。 But, in cases with poorly written code, you may see a different behavior the first time a page is loaded vs. subsequent times when some of the resources are now cached. 但是,在代码编写不佳的情况下,您可能会在第一次加载页面时看到不同的行为,而在随后的某些时间现在缓存某些资源。


To fix your specific code, you need to program asynchronously. 要修复您的特定代码,您需要异步编程。 That means using completion handlers for asynchronous operations like Ajax calls and calling callbacks to notify other code when asynchronous operation are done. 这意味着对异步操作(例如Ajax调用)使用完成处理程序,并在完成异步操作时调用回调以通知其他代码。

For example, your getArticleCount() function is asynchronous. 例如,您的getArticleCount()函数是异步的。 It will finish its Ajax operation some time AFTER the getArticleCount() has already returned. getArticleCount()返回之后的一段时间内,它将完成其Ajax操作。 You can change it to accept a callback like this: 您可以更改它以接受这样的回调:

function getArticleCount(callback) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            callback(xmlhttp.responseText);
        }
    };
    xmlhttp.open("GET", "getArticleCount.php", true);
    xmlhttp.send();
}

And, then you use it like this: 然后,您可以像这样使用它:

getArticleCount(function(cnt) {
    // in here you can use the article count
});

As for your .fadeOut() and .fadeIn() operations, those are not native DOM methods so you can't call them on DOM objects like you are trying to do. 至于.fadeOut().fadeIn()操作,它们不是本机DOM方法,因此您不能像尝试那样在DOM对象上调用它们。 It appears that you are attempting to use the jQuery methods with this name. 看来您正在尝试使用具有此名称的jQuery方法。 To do, you must load jQuery into your page and then you must create jQuery objects that contain the relevant DOM objects and call .fadeOut() and .fadeIn() on the jQuery objects, not on the DOM objects. 为此,必须将jQuery加载到页面中,然后必须创建包含相关DOM对象的jQuery对象,并在jQuery对象而不是DOM对象上调用.fadeOut().fadeIn()


Your loadArticles() function can be fixed by putting the ajax calls inside an internal function inside the method. 您可以通过将ajax调用放在方法内部的内部函数中来修复您的loadArticles()函数。 This will allow each ajax operation you are starting to have it's own separate variables rather than having all of them collide and try to use the same variables. 这将使您开始的每个ajax操作都拥有自己的单独变量,而不是让它们全部冲突并尝试使用相同的变量。 You can do that by using an IIFE (Immediately Invoked Function Expression) inside your for loop like this: 您可以通过在for循环中使用IIFE(立即调用函数表达式)来for此目的for如下所示:

function loadArticles() {
    for (i = 1; i <= article_count; i++) {
        (function() {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    var news = document.createElement("iframe");
                    news.className = "news";
                    news.src = "articles/" + xmlhttp.responseText;
                    news.style.zIndex = 0 - i;
                    var container = document.getElementById("news");
                    container.appendChild(news);
                }
            };
            xmlhttp.open("GET", "getArticles.php?q=" + i, true);
            xmlhttp.send();
        })();
    }
}

Note, because Ajax operations have an indeterminate timing, this code does not guarantee that the items you are adding to the page will be added in any particular order. 注意,由于Ajax操作的时间不确定,因此此代码不保证您要添加到页面的项目将以任何特定顺序添加。 They are more than likely to be added in the order of your for loop, but that is not guaranteed. 它们很可能按照for循环的顺序添加,但这不能保证。 If the serve happens to be quicker with one of the requests vs. another, it might finish first and get added to the page first, even though it was not the first one requested. 如果一次投放恰好与另一次投放相比更快,即使它不是第一个请求,它也可能先完成并首先添加到页面中。


And, since your resize() function appears to use jQuery, you will find it a TON easier to use jQuery's ajax support rather than coding your own Ajax calls. 而且,由于您的resize()函数似乎使用jQuery,因此使用jQuery的ajax支持比编码自己的Ajax调用要容易得多。 Plus with jQuery Ajax, you can use the built-in promise interface to make your asynchronous programming and error handling substantially easier. 加上jQuery Ajax,您可以使用内置的promise接口使异步编程和错误处理变得更加容易。

The reason removing the alert calls in your code made it not work anymore is because your functions getArticleCount() , loadArticles() are making asynchronous requests for data. 删除代码中的alert调用使其不再起作用的原因是因为您的函数getArticleCount()loadArticles()正在对数据进行异步请求。 Alert popups made the program halt, while the AJAX request was off retrieving data and it had returned the result by the time you closed the alert popup. 警报弹出窗口使程序暂停,而AJAX请求关闭了数据检索,并在您关闭警报弹出窗口时返回了结果。

You can change those 2 functions to execute a callback function as a way to let other functions know that it's finished: 您可以更改这两个函数以执行回调函数,以让其他函数知道它已完成:

function init() {
    getArticleCount(function() {
        // finished getting article count
        loadArticles(function() {
            // finished loading articles
            changeSlide();
            resize();
            resize();
        });
    });
}

function getArticleCount(callback) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            article_count = xmlhttp.responseText;
            callback(); // done
        }
    };
    xmlhttp.open("GET", "getArticleCount.php", true);
    xmlhttp.send();
}

function loadArticles(callback) {
    for(i = 1; i <= article_count; i++) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {

            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                var news = document.createElement("iframe");
                news.className = "news";
                news.src = "articles/" + xmlhttp.responseText;
                news.style.zIndex = 0 - i;

                var container = document.getElementById("news");
                container.appendChild(news);
                callback(); // done
            }

        };
        xmlhttp.open("GET", "getArticles.php?q=" + i, true);
        xmlhttp.send();

    }
}

As a side note, you can debug using the browser Developer Tools, and use console.log() and debugger; 附带说明,您可以使用浏览器开发人员工具进行调试,并使用console.log()debugger;

XMLHttpRequest is an asynchronous call. XMLHttpRequest是一个异步调用。

You make a getArticleCount request in order to get a count of articles. 您发出getArticleCount请求以获取文章计数。
Then, you have a loop: 然后,您有一个循环:

for (i = 1; i <= article_count; i++) {

getArticleCount request didn't complete by the time of this loop, and article_count is still equal to zero. 在此循环时, getArticleCount请求尚未完成,并且article_count仍等于零。 You need to use onreadystatechange and move your consequent dependent calls into callbacks: 您需要使用onreadystatechange并将随后的依赖调用移到回调中:

function init() {
    getArticleCount();
}

function getArticleCount() {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            article_count = xmlhttp.responseText;
            loadArticles();
        }
    };

    xmlhttp.open("GET", "getArticleCount.php", true);
    xmlhttp.send();
}

function loadArticles() {
    var container = document.getElementById("news");

    for(i = 1; i <= article_count; i++) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                var news = document.createElement("iframe");

                news.className = "news";
                news.src = "articles/" + xmlhttp.responseText;
                news.style.zIndex = 0 - i;

                container.appendChild(news);
            }

        };

        xmlhttp.open("GET", "getArticles.php?q=" + i, true);
        xmlhttp.send();
    }
}

Anyway, you have some architectural problems: 无论如何,您有一些体系结构问题:

  • Making count request apart from data requests is redundant. 将计数请求与数据请求分开是多余的。 Moreover, the value could change while you execute your articles. 此外,执行文章时该值可能会更改。
  • Making many HTTP requests will cause a huge performance drop. 发出许多HTTP请求将导致性能大幅下降。

You need to make a single PHP-file, which will return a JSON array with all articles. 您需要制作一个PHP文件,该文件将返回包含所有文章的JSON数组。 Then, you will able to work with its length and every item, and it will work much faster while not causing any synchronization problems. 然后,您将能够与它的工作length和每一个项目,它会工作更快,而不会造成任何同步问题。

The alerts are (somewhat) syncronizing your ajax calls. 这些警报正在(某种程度上)同步您的ajax调用。 You have dependencies between getArticleCount and loadArticles . 您在getArticleCountloadArticles之间具有依赖loadArticles The alert() at the top of loadArticles is causing execution to pause until you cancel the alert. loadArticles顶部的alert()导致执行暂停,直到您取消警报。 In that time the AJAX request to "getArticleCount.php" has completed and assigned a value to article_count . 到那时,对“ getArticleCount.php”的AJAX请求已完成,并为article_count分配了一个值。

Without the alerts pausing execution, your code is non-deterministic b/c AJAX calls can not be strung together in a procedural fashion and behave synchronously. 如果没有警报暂停执行,则您的代码是不确定的b / c AJAX调用,无法以过程方式串在一起并且无法同步运行。 You will need to use a functional programming style to get AJAX calls to behave synchronously. 您将需要使用函数式编程样式来获取AJAX调用以使其行为同步。

For example you could make write it like this 例如,您可以像这样写

function init() {

    getArticleCount(loadArticles);
    resize();
    resize();

}

function getArticleCount(callback) {

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {

    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        //callback will be loadArticles()
        callback.call(null, xmlhttp.responseText);

    }

    xmlhttp.open("GET", "getArticleCount.php", true);
    xmlhttp.send();

}

function loadArticles(articleCnt) {

    for(i = 1; i <= articleCnt; i++) {

        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {

            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {

                var news = document.createElement("iframe");
                news.className = "news";
                news.src = "articles/" + xmlhttp.responseText;
                news.style.zIndex = 0 - i;

                var container = document.getElementById("news");
                container.appendChild(news);
                if(i == i){
                    //modify function to use passed in count instead of global
                    changeSlides(articleCnt);
                }
            }

        };

        xmlhttp.open("GET", "getArticles.php?q=" + i, true);
        xmlhttp.send();


    }

} }

When calling getArticleCount the callback parameter would be loadArticles which has been modified to accept the article_count as a parameter instead of using the global. 调用getArticleCountcallback参数将是loadArticles ,该参数已被修改为接受article_count作为参数,而不是使用全局参数。 The above code will fix your issue. 上面的代码将解决您的问题。 You should modify other functions to take a local article count and stop relying on the global. 您应该修改其他功能以获取本地商品计数并停止依赖全局商品。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM