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

Why do blank alert statements affect execution of other code?

本文关键字:其他 代码 执行 影响 空白 语句 为什么      更新时间:2023-09-26

我正在一个网站的首页上工作,该网站将有一个显示新闻文章的部分。文章每10秒就会淡出下一篇。由于某些原因,代码只有在包含两个alert()语句的情况下才能正确执行(请记住,它还没有完全完成,因此可能会有其他错误)。这些以前只是用来调试的,但现在看来,它们似乎有一些功能用途。如果没有它们,代码将给出不同的结果(如果有的话)。我主要是一名Java程序员,所以JavaScript alert()语句可能有一些我不熟悉的特性。我注意到的另一件奇怪的事情是,有时我会多次运行代码而不做任何更改,并得到不同的结果。我在loadArticles()函数中使用了一些alert()语句来输出I的值,偶尔会在不更改代码的情况下得到不同的结果。到目前为止,我唯一的想法是,我的计算机需要时间来运行允许其他进程完成的语句,但不应该涉及任何多线程。

init()函数是在HTML的onload中调用的,页面中央有一个id="news"的div。

最重要的问题是,如果有人能帮我解释为什么我有时无法让文章淡出,我会得到额外的赞扬。我很确定这与文章或容器为null有关,但我还没有时间讨论这个问题。

这是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());
}

您的代码中有很多错误,主要与Ajax调用的异步性质有关。您将需要更多关于使用异步操作进行编程的知识,以编写正确运行、可靠和一致的代码。

alert()语句更改异步操作的相对时间(例如Ajax调用与其他代码运行的时间)

通常情况下,完全停止使用alert()语句作为调试工具,因为它会对时间产生太大影响。相反,请使用console.log()语句。由于console.log()只是输出到控制台,根本不阻止Javascript线程的执行,所以它对事情的时间影响不会像alert()语句那样大。

这里有一个简单的例子来向您展示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");
}

使用alert语句,您将在控制台中获得image is done loading。如果没有警报,您将获得image is not yet done loading。该警报已更改了代码流。


另一件可能影响代码时间的事情是资源是否在浏览器缓存中,或者必须通过网络加载。在几乎所有情况下,只有在知道资源已加载时才使用资源的正确编写的代码在任何一种情况下都将继续工作。但是,在代码编写不好的情况下,您可能会在第一次加载页面时看到与随后缓存某些资源时不同的行为。


要修复您的特定代码,您需要异步编程。这意味着为异步操作(如Ajax调用)使用完成处理程序,并在异步操作完成时调用回调来通知其他代码。

例如,getArticleCount()函数是异步的。它将在getArticleCount()返回后的某个时间完成Ajax操作。您可以更改它以接受这样的回调:

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

然后,你这样使用它:

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

至于您的.fadeOut().fadeIn()操作,它们不是本机DOM方法,因此您不能像尝试这样在DOM对象上调用它们。看起来您正在尝试使用具有此名称的jQuery方法。为此,必须将jQuery加载到页面中,然后必须创建包含相关DOM对象的jQuery对象,并在jQuery对象上调用.fadeOut().fadeIn(),而不是在DOM对象上。


可以通过将ajax调用放在方法内部的内部函数中来修复loadArticles()函数。这将允许您开始的每个ajax操作都有自己的独立变量,而不是让所有变量发生冲突并尝试使用相同的变量。您可以通过在for循环中使用IIFE(立即调用函数表达式)来实现这一点,如下所示:

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

请注意,由于Ajax操作的时间不确定,因此此代码不能保证将要添加到页面中的项按任何特定顺序添加。它们很可能会按照for循环的顺序添加,但这并不能保证。如果其中一个请求的服务速度比另一个请求快,它可能会先完成并首先添加到页面中,即使它不是第一个请求的。


而且,由于resize()函数似乎使用jQuery,您会发现使用jQuery的ajax支持比编写自己的ajax调用更容易。再加上jQueryAjax,您可以使用内置的promise接口来简化异步编程和错误处理。

删除代码中的alert调用使其不再工作的原因是因为函数getArticleCount()loadArticles()正在对数据进行异步请求。警报弹出窗口使程序停止,而AJAX请求已停止检索数据,并且在您关闭警报弹出窗口时已返回结果。

您可以更改这两个函数来执行回调函数,以此让其他函数知道它已经完成:

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

附带说明一下,您可以使用浏览器Developer Tools进行调试,并使用console.log()debugger;

XMLHttpRequest是一个异步调用。

您提出getArticleCount请求以获取文章数
然后,你有一个循环:

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

到该循环时,getArticleCount请求尚未完成,并且article_count仍然等于零。您需要使用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();
    }
}

无论如何,您有一些体系结构问题:

  • 将计数请求与数据请求分开是多余的。此外,在执行文章时,值可能会发生变化
  • 发出许多HTTP请求将导致性能大幅下降

您需要制作一个PHP文件,该文件将返回一个包含所有文章的JSON数组。然后,您将能够处理它的length和每一项,并且它将更快地工作,同时不会引起任何同步问题。

警报(在某种程度上)同步了您的ajax调用。getArticleCountloadArticles之间存在依赖关系。loadArticles顶部的alert()导致执行暂停,直到您取消警报。在这段时间里,对"getArticleCount.php"的AJAX请求已经完成,并为article_count分配了一个值。

如果没有暂停执行的警报,您的代码是不确定的b/c AJAX调用就不能以过程的方式串在一起并同步执行。您将需要使用函数式编程风格来获得AJAX调用的同步行为。

例如,你可以把它写成这样的

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

    }

}

当调用getArticleCount时,callback参数将是loadArticles,其已被修改为接受article_count作为参数而不是使用全局。以上代码将解决您的问题。您应该修改其他函数以获取本地文章计数,并停止依赖全局。