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

时间:2015-11-05 03:03:30

标签: javascript jquery html

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

init()函数在HTML中的onload中调用,并且有一个id =" news"位于页面中心的某个地方。

除了主要问题之外,任何可以帮助我的人都可以获得额外的功劳,因为我有时候不会让文章淡入淡出。我很确定它与文章或容器是空的有关,但我还没有时间去做。

以下是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());

}

4 个答案:

答案 0 :(得分:6)

您的代码中存在许多错误,主要与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调用。再加上jQuery Ajax,您可以使用内置的promise接口来简化异步编程和错误处理。

答案 1 :(得分:2)

删除代码中的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();

    }
}

作为旁注,您可以使用浏览器开发人员工具进行调试,并使用console.log()debugger;

答案 2 :(得分:0)

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和每个项目,并且它可以更快地工作更多,同时不会导致任何同步问题。

答案 3 :(得分:0)

警报(有点)同步你的ajax调用。您有getArticleCountloadArticles之间的依赖关系。 alert()顶部的loadArticles导致执行暂停,直到您取消警报。在那段时间,“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作为参数而不是使用全局。上面的代码将解决您的问题。您应该修改其他功能以获取本地文章计数并停止依赖全局。