【温故而知新-Javascript】使用 Ajax

Ajax 是现代Web 应用程序开发的一项关键工具。它让你能向服务器异步发送和接收数据,然后用 Javascript 解析。 Ajax 是 Asynchronous JavaScript and XML (异步JavaScript 与XML)的缩写。

Ajax 核心规范的名称继承于用来建立和发起请求的 Javascript 对象:XMLHttpRequest 。这个规范有两个等级。所有主流浏览器都实现了第一级,它代表了基础级别的功能。第二级扩展了最初的规范,纳入了额外的事件和一些功能来让它更容易与 form 元素协作,并且支持一些相关规范。

 

1. Ajax起步

Ajax 的关键在于 XMLHttpRequest 对象,而理解这个对象的方法是看个例子。下面代码展示了 XMLHttpRequest 对象的基本用法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用XMLHttpRequest对象</title>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<div id="target">
    Press a button
</div>

<script type="application/javascript">
    var buttons = document.getElementsByTagName("button");
    for(var i=0; i<buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    //脚本会调用此函数以响应 button 控件的 click 事件
    function  handleButtonPress(e){
        //创建一个新的 XMLHttpRequest 对象
        var httpRequest = new XMLHttpRequest();
        //给 onreadystatechange 事件设置一个事件处理器
        httpRequest.onreadystatechange = handleResponse;
        //使用 open 方法来指定 HTTP 方法和需要请求的 URL (即告诉 httpRequest 对象你想要做的事)
        httpRequest.open("GET", e.target.innerHTML+".html");
        //这里没有向服务器发送任何数据,所以 send 方法无参数可用
        httpRequest.send();
    }

    //处理响应
    //一旦脚本调用了 send 方法,浏览器就会在后台发送请求到服务器。因为请求是在后台处理的,所以Ajax 依靠事件来通知这个请求的进展情况。
    function handleResponse(e){
        //当 onreadystatechange 事件被触发后,浏览器会把一个 Event 对象传递给指定的处理函数,target 属性则会被设为与此事件关联的XMLHttpRequest
        if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){ //请求成功
            document.getElementById("target").innerHTML = e.target.responseText; //显示被请求文档的内容
        }
    }
</script>
</body>
</html>

当某个按钮被按下后,示例中的脚本会载入另一个HTML文档,并让它称为div元素的内容。其他的文档一共三个,分别对应button元素上的说明标签:apples.html、lemons.html 和 bananas.html。

此例的显示效果如下图所示:

这三个额外的文档非常简单,其中apples.html 如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Apples</title>
    <style>
        img { float:left;padding:2px;margin:5px;border: medium double black;background-color: lightgrey; width: 100px;height: 100px;}
    </style>
</head>
<body>
<p>
    <img src="../imgs/apple.png"/>
    Page for apples.
</p>
</body>
</html>

随着用户点击各个水果按钮,浏览器会异步执行并取回所请求的文档,而主文档不会被重新加载。这就是典型的 Ajax 行为。

 

2. 使用 Ajax 事件

建立和探索一个简单的示例之后,可以开始深入了解 XMLHttpRequest 对象支持的功能,以及如何在请求中使用它们了。起点就是第二级规范里定义的那些额外事件:

这些事件大多数会在请求的某一特定时间点上触发。 readystatechange 和 progress 这两个事件是例外,它们可以多次触发以提供进度更新。

调度这些事件时,浏览器会对 readystatechange 事件使用常规的 Event 对象,对其他事件则使用 ProgressEvent 对象。 ProgressEvent 对象定义了 Event 对象的所有成员,并增加了下图中介绍的这些成员:

下面代码展示了如何使用这些事件: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用XMLHttpRequest定义的一次性事件</title>
    <style>
        table {margin: 10px;border-collapse: collapse; float: left;}
        div{margin: 10px;}
        td,th{padding: 4px;}
    </style>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<table id="events" border="1"></table>
<div id="target">
    Press a button
</div>

<script type="application/javascript">
    var buttons = document.getElementsByTagName("button");
    for(var i=0; i<buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;
    function  handleButtonPress(e){
        clearEventDetails();
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;

        httpRequest.onerror = handleError;
        httpRequest.onload = handleLoad;;
        httpRequest.onloadend = handleLoadEnd;
        httpRequest.onloadstart = handleLoadStart;;
        httpRequest.onprogress = handleProgress;

        httpRequest.open("GET", e.target.innerHTML+".html");
        httpRequest.send();
    }

    function handleResponse(e){
        displayEventDetails("readystate("+httpRequest.readyState+")")
        if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){
            document.getElementById("target").innerHTML = e.target.responseText;
        }
    }

    function handleError(e){ displayEventDetails("error",e);}
    function handleLoad(e){ displayEventDetails("load",e);}
    function handleLoadEnd(e){ displayEventDetails("loadend",e);}
    function handleLoadStart(e){ displayEventDetails("loadstart",e);}
    function handleProgress(e){ displayEventDetails("progress",e);}

    function clearEventDetails(){
        document.getElementById("events").innerHTML = "<tr><th>Event</th><th>lengthComputable</th><th>loaded</th><th>total</th>";
    }

    function displayEventDetails(eventName,e){
        if(e){
            document.getElementById("events").innerHTML
                    +="<tr><td>"+eventName+"</td><td>"+ e.lengthComputable+"</td><td>"+ e.loaded+"</td><td>"+ e.total+"</td></tr>";
        }else {
            document.getElementById("events").innerHTML += "<tr><td>"+eventName+"</td><td>NA</td><td>NA</td><td>NA</td></tr>";
        }
    }
</script>
</body>
</html>

这是之前示例的一种变型,为一些事件注册了处理函数,并在一个 table 元素里为处理的每个事件都创建了一条记录。从下图中可以看到 Firefox 浏览器是如何触发这些事件的。

3. 处理错误

使用 Ajax 时必须留心两类错误。它们之间的区别源于视角不同。

第一类错误是从 XMLHttpRequest 对象的角度看到的问题:某些因素阻止了请求发送到服务器。例如 DNS 无法解析主机名,连接请求被拒绝,或者URL无效。

第二类问题是从应用程序的角度看到的问题,而非 XMLHttpRequest 对象。它们发生于请求成功发送至服务器,服务器接收请求、进行处理并生成响应,但该响应并不指向你期望的内容。例如,如果请求的URL 不存在,这类问题就会发生。

有三种方式可以处理这些错误,如下面代码所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>处理Ajax错误</title>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
    <button id="badhost">Bad Host</button>
    <button id="badurl">Bad URL</button>
</div>
<div id="target">Press a button</div>
<div id="errormsg"></div>
<div id="statusmsg"></div>

<script type="application/javascript">
    var buttons = document.getElementsByTagName("button");
    for(var i = 0; i < buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;

    function handleButtonPress(e){
        clearMessages();
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.onerror = handleError;
        try{
            switch(e.target.id){
                case "badhost":
                    httpRequest.open("GET","http://www.ycdoitt.com/nopage.html")
                    break;
                case "badurl":
                    httpRequest.open("GET","http://")
                    break;
                default:
                    httpRequest.open("GET", e.target.innerHTML + ".html")
                    break;
            }
            httpRequest.send();
        }catch(error){
            displayErrorMsg("try/catch",error.message+"("+error.name+")")
        }
    }

    function handleError(e){
        displayErrorMsg("Error event",httpRequest.status + httpRequest.statusText);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4){
            var target = document.getElementById("target");
            if(httpRequest.status == 200){
                target.innerHTML = httpRequest.responseText;
            }else{
                document.getElementById("statusmsg").innerHTML
                        = "Status:" + httpRequest.status +" "+ httpRequest.statusText;
            }
        }
    }

    function displayErrorMsg(src,msg){
        document.getElementById("errormsg").innerHTML = src + ": " + msg;
    }

    function clearMessages(){
        document.getElementById("errormsg").innerHTML = "";
        document.getElementById("statusmsg").innerHTML = "";
    }
</script>
</body>
</html>

此例演示结果如下:

 

3.1 处理设置错误

需要处理的第一类问题是向 XMLHttpResquest 对象传递了错误的数据,比如格式不正确的 URL 。它们极其容易发生在生成基于用户输入的URL 时。为了模拟这类问题,上面文档中有添加一个标签 Bad URL (错误的URL)的button 。按下这个按钮会以以下形式调用 open 方法:

 httpRequest.open("GET","http://")

这是一种会阻止请求执行的错误,而 XMLHttpRequest 对象会发生这类事件时抛出一个错误。这就意味着需要用一条 try...catch 语句来围住设置请求的代码,就像这样:

       try{
            ...
            httpRequest.open("GET","http://")
            ...
            httpRequest.send();
        }catch(error){
            displayErrorMsg("try/catch",error.message)
        }        

catch 子句让你有机会从错误中恢复。可以选择提示用户输入一个值,也可以回退至默认的URL ,或是简单地丢弃这个请求。 在这个例子中,仅仅调用了 displayErrorMsg 函数来显示错误消息。

 

3.2 处理请求错误

第二类错误发生在请求已生成,但其他方面出错时。为了模拟这类问题,在示例中添加了一个标签为 Bad Host (错误主机)的按钮。当这个按钮被按下后,就会调用 open 方法访问一个不可用的 URL:

httpRequest.open("GET","http://www.ycdoitt.com/nopage.html")

这个URL 存在两个问题。第一个问题是主机名不能被 DNS 解析,因此浏览器无法生成服务器连接。这个问题知道 XMLHttpRequest 对象开始生成请求时才会变得明显,因此它会以两种方式发出错误信号。如果你注册了一个 error 事件的监听器,浏览器就会向你的监听函数发送一个 Event 对象。以下是示例中使用的函数:

function handleError(e){
        displayErrorMsg("Error event",httpRequest.status + httpRequest.statusText);
    }

当这类错误发生时,能从 XMLHttpRequest 对象获得何种程度的信息取决于浏览器,遗憾的是大多数情况下,会得到的值为 0的 status和空白的 statusText 值。

 第二个问题是URL和生成请求的具有不同的来源,在默认情况下这是不允许的。你通常只能向载入脚本的同源URL发送Ajax请求。浏览器报告这个问题时可能会抛出 Error 或者触发error事件,不同浏览器的处理方法不尽相同。不同浏览器还会在不同的时点检查来源,这就意味着不一定总是能看到浏览器对同一问题突出显示。可以使用跨站资源规范(CORS,Cross-Origin Resource Sharing)来绕过同源限制。

 

3.3 处理应用程序错误

最后一类错误发生于请求成功完成(从XMLHttpRequest对象的角度看),但没有返回你想要的数据时。为了制造这类问题,在上面示例中添加一个说明标签为 cucumber 的 button 。按下这个按钮会生成类似于 apples、cherries 和 bananas 按钮那样的请求URL,但是在服务器上不存在 cucumber.html 这个文档。

这一过程本身没有错误(因为请求已完成),需要根据 status属性来确定发生了什么。当请求某个存在的文档时,会获得404这个状态码,它的意思是服务器无法找到请求的文档。可以看到示例是如何处理200(意思是OK)以外的状态码的:

            if(httpRequest.status == 200){
                target.innerHTML = httpRequest.responseText;
            }else{
                document.getElementById("statusmsg").innerHTML
                        = "Status:" + httpRequest.status +" "+ httpRequest.statusText;
            }

在这个例子中,只是简单的显示了status和statusText的值。而在真正的应用程序里,需要以一种有用且有意义的方式进行恢复(比如显示备用内容或警告用户有问题,具体看哪种更适合应用程序)。

 

4. 获取和设置标头

使用XMLHttpRequest对象,可以设置发送给服务器的请求标头(Header)和读取服务器响应里的标头。

 

4.1 覆盖请求的HTTP方法

通常不需要添加或修改Ajax请求里的标头。浏览器知道需要发送些什么,服务器也知道如何进行响应。不过,有几种情况例外。第一种是 X-HTTP-Method-Override 标头。

HTTP标准通常被用于在互联网上请求和传输HTML文档,它定义了许多方法。大多数人都知道GET和POST,因为它们的使用最为广泛。不过还存在其他一些方法(包括PUT和DELETE),这些HTTP方法用来给向服务器请求的URL赋予意义,而且这种用法正在呈现上升趋势。举个例子,假如想查看某条用户记录,可以生成这样一个请求:

httpRequest.open("GET","http://myserver/records/freeman/adam");

 这里只展示了HTTP方法和请求的URL。要使这个请求能顺利工作,服务器端必须由应用程序能理解这个请求,并将它转变成一段合适的数据以发送回服务器。如果想删除数据,可以这么写:

httpRequest.open("DELETE","http://myserver/records/freeman/adam");

此处的关键在于通过HTTP方法表达出你想让服务器做什么,而不是把它用某种方式编码进URL。

以这种方式使用HTTP方法的问题在于:许多主流的Web技术只支持GET和POST,而且不少防火墙只允许GET和POST请求通过。有一种惯用的做法可以规避这个限制,就是使用 X-HTTP-Method-Override标头来指定想要使用的HTTP方法,但形式上市在发送一个POST请求。代码演示如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设置一个请求标头</title>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<div id="target">Press a button</div>

<script>
    var buttons = document.getElementsByTagName("button");
    for(var i = 0; i < buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;
    function handleButtonPress(e){
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("GET", e.target.innerHTML+".html");
        httpRequest.setRequestHeader("X-HTTP-Method-Override","DELETE");
        httpRequest.send();
    }

    function handleError(e){
        displayErrorMsg("Error event",httpRequest.status+httpRequest.statusText);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("target").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

在这个例子中,有使用XMLHttpRequest对象上的setRequestHeader方法来表明想让这个请求以HTTP DELETE方法的形式进行处理。请注意我在调用open方法之后才设置了这个标头。如果试图在open方法之前使用setRequestHeader方法,XMLHttpRequest对象就会抛出一个错误。

PS:覆盖HTTP需要服务器端的Web应用程序框架能理解X-HTTP-Method-Override这个惯例,并且你的服务器端应用程序要设置成能寻找和理解那些用的较少的HTTP方法。

 

4.2 禁用内容缓存

第二个可以添加到Ajax请求上的有用标头是Cache-Control,它在编写和调试脚本时尤其有用。一些浏览器会缓存通过Ajax请求所获得的内容,在浏览会话期间不会再请求它。对在前面的例子而言,意味着 apples.html、cherries.html和bananas.html 上的改动不会立即反映到浏览器中。下面代码展示了可以如何设置标头来避免这一点:

        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("GET", e.target.innerHTML+".html");
        httpRequest.setRequestHeader("Cache-Control","no-cache");
        httpRequest.send();

设置标头的方式和之前的例子一样,但这次用到的标头是 Cache-Control,而想要的值是 no-cache。放置这条语句后,如果通过Ajax请求的内容发生了改变,就会在下一次请求文档时体现出来。

 

4.3 读取响应标头

可以通过 getResponseHeader 和 getAllResponseHeaders 方法来读取服务器响应某个Ajax请求时发送的HTTP标头。在大多数情况下,你不需要关心标头里有什么,因为它们是浏览器和服务器之间交互事务的组成部分。下面代码展示了如何使用这个属性:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="width=device-width,user-scalable=no" name="viewport" />
    <meta name="author" content="叶超Luka" />
    <meta name="description" content="A simple example" />
    <title>读取响应标头</title>
    <link href="../img/ycdoit.ico" type="image/x-icon" rel="shortcut icon" />
    <style>
        #allheaders,#ctheader{border: medium solid black;padding: 2px;margin: 2px;}
    </style>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<div id="ctheader"></div>
<div id="allheaders"></div>
<div id="target">Press a button</div>

<script>
    var buttons = document.getElementsByTagName("button");
    for(var i = 0; i < buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;
    function handleButtonPress(e){
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("GET", e.target.innerHTML+".html");
        httpRequest.setRequestHeader("Cache-Control","no-cache");
        httpRequest.send();
    }

    function handleResponse(){
        if(httpRequest.readyState==2){
            document.getElementById("allheaders").innerHTML = httpRequest.getAllResponseHeaders();
            document.getElementById("ctheader").innerHTML = httpRequest.getResponseHeader("Content-Type");
        }else if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("target").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

效果图如下:

根据此图可以看出开发服务器正在运行的Web服务器软件是 IntelliJ IDEA 15.0.4,最后修改 apples.html 文档的时间是8月17日。

 

5. 生成跨源Ajax请求

默认情况下,浏览器限制脚本只能在它们所属文档的来源内生成Ajax请求。而来源由URL中的协议、主机名和端口号组成。这就意味着当从http://titan 载入了一个文档后,文档内含的脚本通常无法生成对 http://titan:8080 的请求,因为第二个URL的端口号是不同的,所以处于文档来源之外。从一个来源到另一个来源的Ajax请求被称为跨源请求(cross-origin request)。

PS:这一策略的目的是降低跨站脚本攻击(cross-site scripting,简称CSS)的风险,即诱导浏览器(或用户)执行恶意脚本。

这个策略的问题在于它一刀切地禁止了跨源请求。幸好,跨源资源共享(Cross-Origin Resource Sharing,CORS)规范提供了一种合法的方式来生成跨源请求。

作为准备,下面代码展示了一个HTML文档,它包含的脚本会尝试生成跨源请求:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>尝试生成跨源请求的脚本</title>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<div id="target">Press a button</div>

<script>
    var buttons = document.getElementsByTagName("button");
    for(var i = 0; i < buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;

    function handleButtonPress(e){
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("GET","http://127.0.0.1:8080/"+ e.target.innerHTML);
        httpRequest.send();
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("target").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

这个例子中的脚本扩展了用户所按按钮的内容,把它附加到 http://127.0.0.1:8080/ 上,然后尝试生成Ajax请求(如 http://127.0.0.1:8080/apples)。这里会从 http://localhost:63342/HTML5/ajax/ajax-06.html 载入此文档,这就意味着脚本正在试图生成一个跨源请求。

脚本尝试连接的服务器运行的是 Node.js。代码如下,把它保存在了一个名为 fruitselector.js 的文件里:

var http = require('http');
http.createServer(function (req,res){
    console.log("[200] "+req.method+" to "+req.url);
    res.writeHead(200,"OK",{
        "Content-Type":"text/html"
        });
    res.write('<html><head><title>Fruit Total</title></head><body>');
    res.write('<p>');
    res.write('You selectd '+req.url.substring(1));
    res.write('</p></body></html>');
    res.end();
}).listen(8080);

这是一个非常简单的服务器:它根据客户端请求的URL生成一小段HTML文档。距离来说,如果客户端请求了 http://127.0.0.1:8080/apples ,那么客户端就会生成并返回下列HTML文档:

<html><head><title>Fruit Total</title></head><body><p>You selectd apples</p></body></html>

按照现在这个样子,show-ajax-css.html 里的脚本无法从服务器获取它想要的数据。试运行,反映问题如下图所示:

所以,解决办法是为服务器返回浏览器的响应信息添加一个标头,代码如下所示:

var http = require('http');
http.createServer(function (req,res){
    console.log("[200] "+req.method+" to "+req.url);
    res.writeHead(200,"OK",{
        "Content-Type":"text/html",
        "Access-Control-Allow-Origin":"http://localhost:63342"
        });
    res.write('<html><head><title>Fruit Total</title></head><body>');
    res.write('<p>');
    res.write('You selectd '+req.url.substring(1));
    res.write('</p></body></html>');
    res.end();
}).listen(8080);

Access-Control-Allow-Origin 标头指定了某个来源应当被允许对此文档生成跨源请求。如果标头里指定的来源于当前文档的来源匹配,浏览器就会加载和处理该响应所包含的数据。

PS:支持CORS要求浏览器必须在联系服务器和获取响应标头之后应用跨源安全策略,这就意味着即使响应因为缺少必要的标头或指定了不同的域而被丢弃,请求也已被发送过了。这种方式和没有实现CORS的浏览器非常不同,后者只会简单的阻挡请求,不会去联系服务器。

给服务器响应添加这个标头之后,show-ajax-css.html 文档中的脚本就能够请求和接收来着服务器的数据了,如下图所示:

 

5.1 使用 Origin 请求标头

作为CORS的一部分,浏览器会给请求添加一个 Origin标头以注明当前文档的来源。可以通过它来更灵活地设置 Access-Control-Allow-Origin 标头的值,代码如下所示:

var http = require('http');
http.createServer(function (req,res){
    console.log("[200] "+req.method+" to "+req.url);
    
    res.statusCode = 200;
    res.setHeader("Content-Type","text/html");
    
    var origin = req.headers["origin"];
    if(origin.indexOf("localhost:63342") > -1){
        res.setHeader("Access-Control-Allow-Origin",origin)
    }
    
    res.write('<html><head><title>Fruit Total</title></head><body>');
    res.write('<p>');
    res.write('You selectd '+req.url.substring(1));
    res.write('</p></body></html>');
    res.end();
}).listen(8080);

这里修改了服务器端的脚本,让它只在请求包含Origin标头并且值里有 localhost:63342 时才设置 Access-Control-Allow-Origin 响应标头。这是一种非常粗略的请求来源检查方式,但可以根据具体项目的上下文环境来调整这种方式,使它更精确。

PS:还可以吧 Access-Control-Allow-Origin 标头设置成一个星号(*),意思是允许任何来源的跨源请求。使用这个设置之前应该仔细考虑这么做的安全隐患。

 

5.2 高级CORS功能

CORS规范定义了许多额外的标头,可用于精细化控制跨域请求,包括限制请求能够使用的HTTP方法。这些高级功能需要进行一次预先请求(preflight request),即浏览器先向服务器发送一个请求来确定有哪些限制,然后再发送请求来获取数据本身。

 

6. 中止请求

XMLHttpRequest 对象定义了一个可以中止请求的方法,如下图所示:

为了演示这个功能,修改 fruitselector.js 这段 Node.js 脚本来引入一个2秒延迟,代码如下所示:

var http = require('http');
http.createServer(function (req,res){
    console.log("[200] "+req.method+" to "+req.url);
    
    res.statusCode = 200;
    res.setHeader("Content-Type","text/html");
    
    setTimeout(function(){        
    var origin = req.headers["origin"];
    if(origin.indexOf("localhost:63342") > -1){
        res.setHeader("Access-Control-Allow-Origin",origin)
    }    
    res.write('<html><head><title>Fruit Total</title></head><body>');
    res.write('<p>');
    res.write('You selectd '+req.url.substring(1));
    res.write('</p></body></html>');
    res.end();
    },2000);
    
    
}).listen(8080);

当服务器接收到一个请求,它会先写入初始的响应标头,暂停3秒钟后再完成整个响应。下面代码展示了如何在浏览器上使用 XMLHttpRequest 的中止功能:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>中止请求</title>
</head>
<body>
<div>
    <button>apples</button>
    <button>bananas</button>
    <button>lemons</button>
</div>
<div>
    <button id="abortbutton">Abort</button>
</div>
<div id="target">Press a button</div>

<script>
    var buttons = document.getElementsByTagName("button");
    for(var i = 0; i < buttons.length; i++){
        buttons[i].onclick = handleButtonPress;
    }

    var httpRequest;

    function handleButtonPress(e){
        if(e.target.id == "abortbutton"){
            httpRequest.abort();
        }else {
            httpRequest = new XMLHttpRequest();
            httpRequest.onreadystatechange = handleResponse;
            httpRequest.onabort = handleAbort;
            httpRequest.open("GET","http://127.0.0.1:8080/"+ e.target.innerHTML);
            httpRequest.send();
            document.getElementById("target").innerHTML = "Request Started";
        }
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("target").innerHTML = httpRequest.responseText;
        }
    }

    function handleAbort(){
        document.getElementById("target").innerHTML = "Request Aborted";
    }
</script>
</body>
</html>

这里给文档添加了一个Abort(中止按钮),它通过调用 XMLHttpRequest 对象上的abort方法来中止进行中的请求。因为在服务器端引入了一段延迟,所以有充足的时间来执行它。

XMLHttpRequest 通过 abort 事件和 readystatechange 事件给出中止信号。在这个例子中,响应了 abort 事件,并更新了id为target的div元素中的内容,以此标明请求已被中止。效果图如下:

 

来源:《HTML5权威指南》(《The Definitive Guide to HTML5》)

转载于:https://www.cnblogs.com/yc-755909659/p/5928050.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1 University students can understand innovation through learning from the past. 2. Students can better review by breaking down complex concepts into smaller components and studying the material in an organized way. 3. When learning from the past to understand innovation, it is important to focus on understanding the big picture and to not get bogged down in the details. ### 回答2: 1. 大学生如何理解温故而知新温故而知新是一种学习方法,它要求我们在学习新知识之前先回顾和巩固已经学过的知识。大学生理解温故而知新意味着要在学习新知识之前,先回顾和复习以前学过的相关知识或基础知识。通过温故,我们能够加深对已有知识的理解和记忆,从而更好地理解和掌握新的知识。 2. 学生如何更好地去复习? 学生要更好地复习,可以采取以下策略: 首先,制定一个合理的复习计划,将要复习的内容分配到不同的时间段,确保每个科目都有足够的时间。 其次,采用多种复习方法,如阅读教材、做练习题、参加讨论等,以帮助加深理解和牢固记忆。 另外,与同学或老师一起讨论复习内容,通过讲解和互动来加深理解。 此外,保持良好的学习习惯,比如及时复习、做好笔记等,能够帮助学生更好地掌握和复习知识。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,需要注意以下几点: 首先,要有针对性,根据自己的学习需求和复习目标,选择性地回顾和复习相关知识点。 其次,要有系统性,将复习内容进行分类整理,形成一个清晰的知识框架,有助于加深理解和记忆。 另外,要关注重难点,重点复习那些相对较难或容易遗忘的知识点,加强对这些内容的学习和理解。 还要有耐心和恒心,温故而知新是一个持续的过程,需要长期坚持和不断巩固。 最后,要善于总结和归纳,通过整理和回顾复习过程中的笔记和练习,提炼出关键概念和思维模式,便于记忆和应用。 ### 回答3: 1. 大学生如何理解温故而知新? 大学生可以理解为通过回顾过去的知识和经验,来获取新的见解和理解。温故是指回顾已经学过的知识,了解其中的原理、概念和重要点。而知新则是指通过对新知识的学习,扩展和更新自己的知识体系。温故而知新相辅相成,是一个持续学习和发展的过程。 2. 学生如何更好地去复习? 学生可以通过以下方式更好地进行复习: - 制定合理的复习计划:根据时间安排和课程难度,合理分配复习时间,确保每个学科都有足够的复习时间。 - 多种复习方法结合:采用不同的学习方式,如阅读教材、做练习题、参与讨论、制作思维导图等,帮助巩固记忆和理解知识。 - 主动参与课堂:积极参与讨论和提问,与同学和老师交流,加深对知识的理解和记忆。 - 不断反思和总结:及时检查自己的复习情况,发现不足和问题,并及时调整学习方法和计划。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,学生需要注意以下几点: - 有目的性地温故:针对具体的知识点或者问题进行回顾,明确自己的学习目标和重点。 - 理解和记忆结合:不仅要理解概念和原理,还要通过多次的复习和记忆,帮助信息在大脑中形成长期记忆。 - 理论联系实际:将学到的知识应用到实际情境中,加深对知识的理解和记忆。 - 及时巩固复习成果:通过做练习题、整理笔记、与同学讨论等方式,巩固复习的成果,确保知识掌握得更牢固。 - 长期持续学习:温故而知新是一个持续的过程,要保持学习的热情和动力,不断更新自己的知识体系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值