觸碰jQuery:AJAX異步詳解


傳送門:異步編程系列目錄……

示例源碼:觸碰jQueryAJAX異步詳解.rar

AJAX 全稱 Asynchronous JavaScript and XML(異步的 JavaScript XML)。它並非一種新的技術,而是以下幾種原有技術的結合體。

1)   使用CSSXHTML來表示。

2)   使用DOM模型來交互和動態顯示。

3)   使用XMLHttpRequest來和服務器進行異步通信。

4)   使用javascript來綁定和調用。

通過AJAX異步技術,可以在客戶端腳本與web服務器交互數據的過程中使用XMLHttpRequest對象來完成HTTP請求(Request)/應答(Response)模型:

1)   不需要用戶等待服務端響應。在異步派發XMLHttpRequest請求后控制權馬上就被返回到瀏覽器。界面不會出現白板,在得到服務器響應之前還可以友好的給出一個加載提示。

2)   不需要重新加載整個頁面。XMLHttpRequest注冊一個回調函數,待服務器響應到達時,觸發回調函數,並且傳遞所需的少量數據。“按需取數據”也降低了服務器的壓力。

3)   不需要使用隱藏或內嵌的框架。XHR對象之前,模擬Ajax通信通常使用hack手段,如使用隱藏的或內嵌的框架(<iframe>標簽)

下面介紹下AJAX中的重要對象:XMLHttpRequest

 

XMLHttpRequest對象(XHR)

XMLHttpRequest是一套可以在JavascriptVbScriptJscript等腳本語言中通過http協議傳送或接收XML及其他數據的一套API

XMLHttpRequest對象首次以ActiveX對象形式在微軟Internet Explorer(IE) 5中以引入。其他瀏覽器制造商在認識到這一對象重要性后也紛紛實現了XMLHttpRequest對象,但是以一個本地JavaScript對象而不是作為一個ActiveX對象實現。而如今,由於安全性、標准等問題,微軟已經在其IE 7中把XMLHttpRequest實現為一個本地JavaScript對象。

 

API

描述

 

客服端請求

open(method,url,async, bstrUser, bstrPassword)

規定請求的類型、URL 以及是否異步處理請求。

1)   method:請求的類型,例如:POSTGETPUTPROPFIND。大小寫不敏感。

2)   url:請求的URL地址,可以為絕對地址也可以為相對地址。

3)   async[可選]true(默認,異步)或 false(同步)。

注釋:當您使用async=false ,JavaScript 會等到服務器響應就緒才繼續執行。如果服務器繁忙或緩慢,應用程序會掛起或停止。此時,不需要編寫onreadystatechange回調函數,把代碼放到 send() 語句后面即可。

4)   bstrUser[可選]:如果服務器需要驗證,此處指定用戶名,如果未指定,當服務器需要驗證時,會彈出驗證窗口。

5)   bstrPassword[可選]:驗證信息中的密碼部分,如果用戶名為空,則此值將被忽略。

getRequestHeader(name)

獲取指定的相應頭部信息

setRequestHeader(name,value)

自定義HTTP頭部信息。需open()方法之后和send()之前調用,才能成功發送請求頭部信息。

傳送門:HTTP 頭部詳解

Accept

瀏覽器能夠處理的媒體類型

Accept-Charset

瀏覽器申明自己接收的字符集

Accept-Encoding

瀏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支持壓縮,支持什么壓縮方法(gzipdeflate

Host

客戶端指定要請求的WEB服務器的域名/IP 地址和端口號

Referer

發出請求的頁面的URI

Content-Type

標明發送或者接收的實體的MIME類型。傳送門:

1HTTP Content-type對照表

2格式:Content-Type: [type]/[subtype]; parameter

X-Requested-With

非標准HTTP頭,只為firefox3標注是否為ajax異步請求,null表示為同步請求。

默認情況下,服務器對POST請求和提交Web表單不會一視同仁,將Content-Type頭部信息設置為application/x-www-form-urlencoded (模擬表單提交)

send(string)

將請求發送到服務器。參數string僅用於POST請求;對於GET請求的參數寫在url后面,所以string參數傳遞null

abort()

調用此方法可取消異步請求,調用后,XHR對象停止觸發事件,不允許訪問任何與響應相關的屬性;

 

服務端響應

onreadystatechange事件

對於異步請求,如果需要對服務器獲取和操作響應結果,則send() 之前,需要為onreadystatechange屬性指定處理方法。該函數用於對服務器響應進行處理。

readyState

存有XMLHttpRequest的狀態。每當readyState改變時,就會觸發onreadystatechange事件。

0 4 發生變化:

0(未初始化)

對象已建立,但是尚未初始化(尚未調用open方法)

1(初始化)

對象已建立,尚未調用send方法

2(發送數據)

send方法已調用,但是當前的狀態及http頭未知

3(數據傳送中)

已接收部分數據,因為響應及http頭不全,這時通過responseBodyresponseText獲取部分數據會出現錯誤

4(完成)

數據接收完畢,此時可以通過responseXmlresponseText獲取完整的回應數據

 

status(數字表示)

返回當前請求的http狀態碼。

傳送門:HTTP狀態碼一覽表(HTTP Status Code

1xx(臨時響應)

表示臨時響應並需要請求者繼續執行操作的狀態代碼。

2xx (成功)

表示成功處理了請求的狀態代碼。Eg200

3xx (重定向)

表示要完成請求,需要進一步操作。通常,這些狀態代碼用來重定向。Eg304

4xx(請求錯誤)

這些狀態代碼表示請求可能出錯,導致服務器無法正常處理。Eg404

5xx(服務器錯誤)

這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。Eg500

 

statusText(字符表示)

返回當前請求的狀態文本egOK  status200

responseText

將響應信息作為字符串返回

responseXML

將響應信息格式化為Xml Document對象並返回

responseBody(只有微軟的IE支持)

將響應信息正文以unsigned byte數組形式返回(二進制數據)

responseStream(只有IE的某些版本支持)

Ado Stream對象(二進制流)的形式返回響應信息

getResponseHeader(name)

從響應信息中獲取指定的http

getAllResponseHeaders()

獲取響應的所有http

overrideMimeType

通常用於重寫服務器響應的MIME類型。Eg,正常情況下XMLHttpRequest只接收文本數據,但我們可以重寫MIME為“text/plain; charset=x-user-defined”,以欺騙瀏覽器避免瀏覽器格式化服務器返回的數據,以實現接收二進制數據。

 

一個簡單的ajax封裝:

var myAjax = {
    // XMLHttpRequest IE7+, Firefox, Chrome, Opera, Safari ;  ActiveXObject IE6, IE5
    xhr: window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),
    get: function (url, callback) {
        this.xhr.open('get', url);
        this.onreadystatechange(callback, this.xhr);
        this.xhr.send(null);        
    },
    post: function (url, data, callback) {
        this.xhr.open('post', url);
        this.xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        this.onreadystatechange(callback, this.xhr);
        this.xhr.send(data);
    },
    onreadystatechange: function (func, _xhr) {
        _xhr.onreadystatechange = function () {
            if (_xhr.readyState == 4) {
                if (_xhr.status == 200) {
                    func(_xhr.responseText);
                }
            }
        }
    }
}

使用:

            $('#btn_nowTime1').bind('click', null
                , function () {
                    myAjax.post('AjaxHandler.ashx', 'func=GetServerTime'
                        , function (data) {
                            if (data)
                                alert(data);
                        }
                    );
                });

 

XMLHttpRequest Level 2

XMLHttpRequest是一個瀏覽器接口,使得Javascript可以進行 HTTP (S) 通信。但是,這個接口一直沒有標准化,每家瀏覽器的實現或多或少有點不同。HTML 5 的概念形成后,W3C 開始考慮標准化這個接口。2008 2 月,提出了XMLHttpRequest Level 2 草案。

1.   老版本的缺點

老版本的XMLHttpRequest對象有以下幾個缺點:

1)   只支持文本數據的傳送,無法用來讀取和上傳二進制文件。

2)   傳送和接收數據時,沒有進度信息,只能提示有沒有完成。

3)   受到"同域限制"Same Origin Policy),只能向同一域名的服務器請求數據。

2.   新版本的功能

新版本的XMLHttpRequest對象,針對老版本的缺點,做出了大幅改進。

1)   可以設置 HTTP 請求的時限。

2)   可以使用FormData對象管理表單數據。

3)   可以上傳文件。

4)   可以請求不同域名下的數據(跨域資源共享,Cross-origin resource sharing,簡稱 CORS)。

5)   可以獲取服務器端的二進制數據。

6)   可以獲得數據傳輸的進度信息。

3.   介紹幾個XMLHttpRequest Leve2 新增的成員

 

超時時限

timeout

設置ajax請求超時時限,過了這個時限,就自動停止 HTTP 請求。

ontimeout事件

ajax超過timeout 時限時觸發的回調函數。

 

指定響應格式

responseType

(默認:“text)在發送請求前,根據您的數據需要,將xhr.responseType設置為“text”、“arraybuffer”、“blob”或“document”。

response

成功發送請求后,xhr的響應屬性會包含DOMStringArrayBufferBlob Document 形式(具體取決於responseTyp的設置)的請求數據。

 

進度信息

progress 事件

XMLHttpRequest對象傳遞數據的時候用來返回進度信息。它分成上傳和下載兩種情況。下載的 progress 事件屬於XMLHttpRequest對象,上傳的 progress 事件屬於XMLHttpRequest.upload對象。即:

xhr.onprogress = updateProgress;

xhr.upload.onprogress = updateProgress;

XHR還新增了與progress事件相關的五個事件:

1)   load 事件:傳輸成功完成。

2)   abort 事件:傳輸被用戶取消。

3)   error 事件:傳輸中出現錯誤。

4)   loadstart事件:傳輸開始。

5)   loadEnd事件:傳輸結束,但是不知道成功還是失敗。

4.   一個新功能實例

1)   接收二進制數據(方法A:改寫MIMEType

老版本的XMLHttpRequest對象,只能從服務器取回文本數據。但我們可以改寫數據的MIMEType將服務器返回的二進制數據偽裝成文本數據,並且告訴瀏覽器這是用戶自定義的字符集

關鍵代碼如下:

服務端

        String str = "二進制數據獲取";
        MemoryStream _memory = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(_memory, str);
        _memory.Position = 0;
        byte[] read = new byte[_memory.Length];
        _memory.Read(read, 0, read.Length);
        _memory.Close();
        context.Response.ContentType = "text/plain";
	// 服務器使用OutputStream輸出二進制流
        context.Response.OutputStream.Write(read, 0, read.Length);

客服端

            $('#btn_mime').bind('click', null
                , function () {
                    $.ajax('AjaxHandler.ashx?func=GetBinaryData',
                        {
                            type: 'get',
                            dataType: 'text',
                            cache: false,
                            mimeType: 'text/plain; charset=x-user-defined', 
                            success: function (data) {
                                if (data) {
                                    var byte = [];
                                    for (var i = 0, len = data.length; i < len; ++i) {
                                        var c = data.charCodeAt(i);
                                        byte[byte.length] = c & 0xff;
                                    }
                                    alert(byte);
                                }
                            }
                        });
                });

瀏覽器會把相應數據當做文本數據接收,所以我們還必須再一個個字節地還原成二進制數據。位運算"c & 0xff",表示在每個字符的兩個字節之中,只保留后一個字節,將前一個字節扔掉。原因是瀏覽器解讀字符的時候,會把字符自動解讀成Unicode 0xF700-0xF7ff 區段。

截圖如下:(測試環境:google Chrome 版本 26.0.1410.43

服務器端返回二進制數據:

image

客服端輸出:

a)   使用mimeType: 'text/plain; charset=x-user-defined'參數。

     image

b)   沒有對服務器的MIME類型進行重寫,導致返回信息被瀏覽器格式化后輸出的二進制數據與服務器不同並且不同瀏覽器格式化后輸出的二進制數據都有差異。

     image

 

2)   接收二進制數據(方法BresponseType屬性)

XMLHttpRequest Level2中,可以使用新增的responseType屬性從服務器取回二進制數據。把responseType設為 blob,表示服務器傳回的是二進制對象。

var xhr = new XMLHttpRequest();
xhr.open ('GET', '/path/to/image.png');
xhr.responseType = 'blob';

接收數據的時候,用瀏覽器自帶的 Blob 對象即可。注意,讀取的xhr.response,而不是xhr.responseText

var blob = new Blob ([xhr.response], {type: 'image/png'});

還可以將responseType設為arraybuffer,把二進制數據裝在一個數組里。然后再遍歷這個數組。

var xhr = new XMLHttpRequest ();
xhr.open ('GET', '/path/to/image.png');
xhr.responseType = "arraybuffer";
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var byteArray = new Uint8Array (arrayBuffer);
    for (vari = 0; i<byteArray.byteLength; i++) {
        // do something
    }
}

5.   更多XMLHttpRequest Level 2新功能描述請看:

1)   XMLHttpRequest 增強功能

2)   XMLHttpRequest Level 2 使用指南

3)   XMLHttpRequest2 新技巧

 

jQuery框架的Ajax

    jQuery是一個快速、簡單的JavaScript library核心理念是write less,do more(寫的更少,做的更多)。它簡化了HTML 文件的traversing,事件處理、動畫、Ajax 互動,從而方便了網頁制作的快速發展。jQuery是為改變你編寫JavaScript 的方式而設計的。更多jQuery科普知識請看:jQuery百度百科(Eg:模塊,歷史版本)

    下面介紹下jQuery框架中ajax相關API:

    版本Jquery-1.7.1.js

 

1.   jQuery.ajax( [url,] options )

    通過 HTTP 請求加載遠程數據。

返回值:$.ajax() 返回jqXHR對象(jqXHR對象:為XMLHttpRequest對象的超集)。可用於手動終止請求abort()、為ajax函數設置額外的回調函數等。

 

ajax內部實現的兩個重要對象:s對象和jqXHR對象。

1)   s對象

由默認設置jQuery.ajaxSettings對象、options參數集合和jQuery.ajaxSetup({})默認設置合並而成s對象。

參數名

描述

 

可由ajaxoptions參數設置

url

(默認: 當前頁地址) 要請求的目的URL地址。

username

password

用於響應HTTP訪問認證請求的用戶名及密碼

type

(默認: "GET") 請求方式 ("POST" "GET")。注意:其它 HTTP 請求方法,如 PUT DELETE 也可以使用,但僅部分瀏覽器支持。

dataType

預期服務器返回的數據類型。如果不指定,jQuery將自動根據 HTTP MIME 信息來智能判斷,比如 XML MIME 類型就被識別為 XML。隨后服務器端返回的數據會根據這個值解析后,傳遞給回調函數。

必須確保網頁服務器報告的 MIME 類型與我們選擇的dataType所匹配。比如說,XML的話,服務器端就必須聲明 text/xml 或者 application/xml 來獲得一致的結果。

可用值:

"xml"

返回 XML 文檔,可用jQuery處理。

"html"

返回純文本 HTML 信息;包含的 script 標簽會在插入dom時執行

"script"

返回純文本 JavaScript 代碼,常常用於跨域請求。不會觸發全局事件和局部事件;只支持GET方式(POST請求會自動轉化為GET請求);默認不啟用緩存(cache:false)

"json"

返回 JSON 數據。JSON 數據是一種能很方便通過 JavaScript 解析的結構化數據。

"jsonp"

JSONP 格式,用於跨域請求。

"text"

返回純文本字符串

其中,text xml 類型返回的數據不會經過處理。數據僅僅簡單的將XMLHttpRequestresponseTextresponseHTML屬性傳遞給 success 回調函數。

如果指定了 script 或者jsonp類型,那么當從服務器接收到數據時,實際上是用了<script>標簽而不是XMLHttpRequest對象。這種情況下,$.ajax() 不再返回一個XMLHttpRequest對象,並且也不會傳遞事件處理函數,比如beforeSend

contentType

(默認: "application/x-www-form-urlencoded")標明發送或者接收的實體的MIME類型。當“非GETHEAD請求”HTTP請求時,會被設置為HTTP頭請求信息。

mimeType

多用途互聯網郵件擴展(MIMEMultipurpose Internet Mail Extensions);用於重寫服務器端響應的MIME類型。

data

發送到服務器的數據。可以是一個查詢字符串,比如 key1=value1&amp;key2=value2 ,也可以是一個映射,比如 {key1: 'value1', key2: 'value2'} 。如果使用了后者的形式,則數據在發送前會通過jQuery.param()函數轉換成查詢字符串。這個處理過程也可以通過設置processData選項為false來回避。

processData

(默認: true) 默認情況下,發送到服務器的數據(即data參數)將被轉換為字符串以配合默認內容類型 "application/x-www-form-urlencoded"。如果要發送 DOM 樹信息或其它不希望轉換的信息,請設置為 false

jQuery中的處理方式:

if ( s.data&&s.processData&&typeofs.data !== "string" ) {
    s.data = jQuery.param(s.data, s.traditional );
}

async

(默認: true) 默認設置下,所有請求均為異步請求。如果需要發送同步請求,請將此選項設置為 false。注意,同步請求將鎖住瀏覽器,用戶其它操作必須等待請求完成才可以執行。

timeout

設置請求超時時間(毫秒)。通過setTimeout(fn,time)實現。

cache

(默認: true)dataType script jsonp時默認為 false。設置為 false 將不緩存此頁面。

當使用GETHEAD方式發送請求時要添加時間戳參數 (net Date()).getTime() 來保證每次發送的URL不同, 可以避免瀏覽器緩存.(只有GETHEAD方式的請求瀏覽器才會緩存)

jQuery中的處理方式:

if ( s.cache === false ) {
    var ts = jQuery.now(),
    // rts = /([?&])_=[^&]*/嘗試替換
    ret = s.url.replace( rts, "$1_=" + ts );
    // rquery = /\?/如果沒有替換任何內容,則把時間戳加到url最后
    s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
}

示例:/AjaxHandler.ashx?func=GetBinaryData&_=1368424995535

ifModified

(默認: false) 僅在服務器數據改變時獲取新數據。通過響應頭If-Modified-SinceIF-None-Match和請求頭Last-ModifiedEtag提高GETHEAD方式請求效率。(只有GETHEAD方式的請求瀏覽器才會緩存)

global

(默認: true) 是否觸發全局 AJAX 事件。設置為 false 將不會觸發全局AJAX 事件:ajaxStartajaxSendajaxSuccessajaxErrorajaxCompleteajaxStop。(比如請求頻繁時可禁用全局AJAX事件提高效率)

context

(默認:true)  這個對象用於設置Ajax相關回調函數的上下文,讓回調函數內this指向這個對象。如果不設定這個參數,那么回調函數中的this就指向調用本次AJAX請求時傳遞的options參數載體“s對象”。但對於全局Ajax事件來說,this都是指向全局事件所綁定的元素。

jsonp

指定獲得jsonp回調函數名的參數名(默認為:callback)。這個值用來替代URL"callback=?"里的"callback"部分,比如{jsonp:'onJsonPLoad'}會替換為將"onJsonPLoad=?"傳給服務器。

jsonpCallback

jsonp請求指定一個回調函數名。jsonpCallback參數一般為字符串,也可接收函數(該函數返回字符串)

默認情況下生成隨機函數名:"jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ) + jQuery.now()

crossDomain

(默認:null)false:同域請求;true跨域請求。

倘若crossDomain標識為null,則jQuery會自動根據本地url、端口來解析。可以根據需求直接賦值來提高性能。

通常情況下由服務器自動解析即可,但如果你想在同一域中強制跨域請求(像JSONP一樣),那么將crossDomaintrue,這允許你將服務器端重定向到另一個域。

scriptCharset

只有當請求時dataType"jsonp""script",並且type"GET"才會用於修改charset

因為此時是動態創建<script>來完成腳本加載,但是如果js中的編碼與頁面的編碼不一致時,js可能加載失敗或者顯示亂碼或者IE下報某符號錯誤。設置此參數就相當於為<script>標簽設置charset屬性。

hearders

(默認:{})  設置HTTP請求頭數據"{:}"此設置發生在:jQuery所有影響HTTP頭的參數(options)設置之后,beforeSend回調函數之前

statusCode

(默認:{})  定義一組HTTP狀態碼與回調函數的映射,當響應的狀態碼有匹配statusCode則會觸發對應回調函數。例如,如果響應狀態是404,將觸發以下警報:

$.ajax({
    statusCode: {404: function() {
       alert('page not found');
    }
});

traditional

如果你想要用傳統的方式來序列化數據,那么就設置為true。請參考$.param()深度遞歸詳解

xhrFields

聲明附加到XMLHttpRequest對象的自定義“key-value”數組。例如,如果需要的話,你可以用它來設置跨域的withCredentialstrue,即:

xhrFields: { withCredentials: true }  

5個局部事件

beforeSenddataFiltersuccesserrorcomplete。(詳見后面事件介紹部分)

 

ajax函數內部解析或內部提供

dataTypes

dataType按空格拆分所得。

isLocal

根據協議確定當前url請求的是否為本地請求。

jQuery中定義默認值為:

isLocal:/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/.test(/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/.exec(url))

hasContent

GETHEAD請求為true,用於處理datacontentType參數。

contents

一個"{類型字符串:正則表達式}"的對象,倘若dataTypes[0]為“*”時,用contents中的正則表達式去匹配contentType,匹配成功則用“類型字符串”覆蓋dataTypes[0]

jQuery內部定義如下:

contents: {
    xml: /xml/,
    html: /html/,
    json: /json/,
    script: /javascript|ecmascript/
}

accepts

瀏覽器能夠處理的媒體類型,其值取決於dataTypes[0]參數。

jQuery內部定義如下:

accepts: {
    xml: "application/xml, text/xml",
    html: "text/html",
    text: "text/plain",
    json: "application/json, text/javascript",
    script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript",
	"*": allTypes    // dataTypes[0]匹配不上時取此值
}

responseFields

jqXHR超集設置“數據類型:屬性對應關系,在返回響應數據時,用於確定創建哪個屬性變量。

jQuery中定義如下:

responseFields: {
    xml: "responseXML",
    text: "responseText"
}

converters

存儲數據類型對應的轉換器,根據dataTypes獲取對應轉換器,用於對響應數據response進行處理。該處理發生在dataFilter回調函數之后。

converters: {
    "* text": window.String,
    "text html": true,
    "text json": jQuery.parseJSON,
    "text xml": jQuery.parseXML,
    "text script": function( text ) {
		    jQuery.globalEval( text );  // 執行腳本
		    return text;
		}
}
     

2)   jqXHR對象

為不同瀏覽器內置的XMLHttpRequest提供了一致的超集。對於XMLHttpRequest之外的傳輸機制,比如JSONP請求,jXHR對象也可以進行處理。

超集與真子集:

如果一個集合S2中的每一個元素都在集合S1中,且集合S1中可能包含S2中沒有的元素,則集合S1就是S2的一個超集。 S1S2的超集,則S2S1的真子集,反之亦然。

    jqXHR對象我們常常使用如下成員,這些成員主要用於ajax的全局事件和局部事件,並且做為$.ajax()函數返回值返回。

jqXHR:{
    readyState
    ,setRequestHeader: function( name, value )
    ,getAllResponseHeaders: function()
    ,getResponseHeader: function( key )
    ,overrideMimeType: function( type )
    ,abort: function( statusText )
    ,responseText
    ,responseXML
}

另外,jqXHR的全部成員如下:

image

    在圖中我們看到一些陌生的函數,比如:done()fail()promise()isResolve()isRejected()then()always()progress()等,都是jQuerydeferred對象API

開發網站的過程中,我們經常遇到某些耗時很長的javascript操作。其中,既有異步的操作(比如ajax讀取服務器數據),也有同步的操作(比如遍歷一個大型數組),它們都不是立即能得到結果的。

    通常的做法是,為它們指定回調函數(callback)。即事先規定,一旦它們運行結束,應該調用哪些函數。但是,在回調函數方面,jQuery的功能非常弱。為了改變這一點,jQuery開發團隊就設計了deferred對象。

簡單說,deferred對象就是jQuery的回調函數解決方案。在英語中,defer的意思是"延遲",所以deferred對象的含義就是"延遲"到未來某個點再執行。 它解決了如何處理耗時操作的問題,對那些操作提供了更好的控制,以及統一的編程接口。

更專業的資源:jQuery的deferred對象詳解

 

2.   jQuery Ajax事件

jQuery框架中,伴隨Ajax請求會觸發若干事件,我們可以訂閱這些事件並在其中處理我們的邏輯。在jQuery中有兩種Ajax事件:局部事件和全局事件。

1)   局部事件(回調函數),在$.ajax()方法的options參數中聲明,可以用來設置請求數據和獲取、處理響應數據。

beforeSend

該函數可在發送請求前修改XMLHttpRequest對象,如添加自定義 HTTP 頭。

簽名:function (jqXHR,s) { }

函數說明:傳入jqXHRs對象

dataFilter

在請求成功之后調用。若狀態碼為304(未修改)則不觸發此回調。

簽名:function (data, dataType) { return newData; }

函數說明:傳入返回的數據、"dataType"參數的值。並且必須返回新的數據傳遞給success回調函數

success

請求成功時觸發。

簽名:function (data,statusText,jqXHR) { }

函數說明:傳入返回的數據、描述狀態的字符串”success”jqXHR對象

error

請求失敗時調用此函數。

簽名:function (jqXHR, textStatus, errorThrown) { }

函數說明:傳入jqXHR對象、描述狀態的字符串”error”、錯誤信息

complete

請求完成后回調函數 (請求成功或失敗之后均調用)

簽名:function (jqXHR, textStatus) { }

函數說明:傳入jqXHR對象、描述狀態的字符串(可能值:"No Transport""timeout""notmodified"---304 ""parsererror""success""error"

        定義方式例如:

$.ajax({
    // ...
    beforeSend: function(){
        // Handle the beforeSend event
    },
    complete: function(){
        // Handle the complete event
    }
    // ...
});

2)   全局事件,每次Ajax請求都會觸發,它會向DOM中的所有元素廣播,你只需為DOM中任意元素bind好全局事件即會觸發(若綁定多次,則會依次觸發為事件注冊的回調函數)

ajaxStart

開始新的Ajax請求,並且此時jQuery對象上沒有其他ajax請求正在進行。

簽名:function(e)

函數說明:傳入事件對象

ajaxSend

當一個Ajax請求開始時觸發

簽名:function(e,jqXHR,s)

函數說明:傳入事件對象、jqXHRs對象

ajaxSuccess

全局的請求成功

簽名:function(e,jqXHR,s,data)

函數說明:傳入事件對象、jqXHRs對象、請求成功返回的相應數據

ajaxError

全局的發生錯誤時觸發

簽名:function(e,jqXHR,s,errorData)

函數說明:傳入事件對象、jqXHRs對象、請求失敗返回的錯誤信息

ajaxComplete

全局的請求完成時觸發

簽名:function(e,jqXHR,s)

函數說明:傳入事件對象、jqXHRs對象

ajaxStop

jQuery對象上正在進行Ajax請求都結束時觸發。

簽名:function(e)

函數說明:傳入事件對象

全局事件在jQuery中的聲明方式:

jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
	    jQuery.fn[ o ] = function( f ){
		return this.on( o, f );
	};
});

    所以我們可以使用下面兩種方式定義全局事件:

// 可以用bind來綁定,用unbind來取消綁定。
$("#loading").bind("ajaxSend", function(){ … });
或者:
$("#loading").ajaxStart(function(){ … });

3)   ajax方法完整的事件流

    image

    image

4)   示例:$.ajax()觸發的事件(局部事件和全局事件)

            // 全局事件
            $("#div_event").ajaxStart(function (e) {
                doAddEvent4textarea('txt_event', '觸發ajaxStart回調函數');
            });
            $("#div_event").ajaxSend(function (e) {
                doAddEvent4textarea('txt_event', '觸發ajaxSend回調函數');
            });
            $("#div_event").ajaxSuccess(function (e, jqXHR, s, data) {
                doAddEvent4textarea('txt_event', '觸發ajaxSuccess回調函數');
            });
            $("#div_event").ajaxError(function (e, jqXHR, s, errorData) {
                doAddEvent4textarea('txt_event', '觸發ajaxError回調函數');
            });
            $("#div_event").ajaxComplete(function (e, jqXHR, s) {
                doAddEvent4textarea('txt_event', '觸發ajaxComplete回調函數');
            });
            $("#div_event").ajaxStop(function (e) {
                doAddEvent4textarea('txt_event', '觸發ajaxStop回調函數');
            });
            // 局部事件
            function bindLocalEvent(e) {
                var textareaid = e.data.textareaid;
                var global = e.data.global;

                $.ajax('AjaxHandler.ashx?func=btn_nowTime_long',
                    {
                        type: 'get',
                        dataType: 'text',
                        global: global,
                        cache: false,
                        beforeSend: function (jqXHR, s) {
                            doAddEvent4textarea(textareaid, '觸發beforeSend回調函數');
                        },
                        dataFilter: function (data, dataType) {
                            doAddEvent4textarea(textareaid, '觸發dataFilter回調函數');
                        },
                        success: function (data, statusText, jqXHR) {
                            doAddEvent4textarea(textareaid, '觸發success回調函數');
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                            doAddEvent4textarea(textareaid, '觸發error回調函數');
                        },
                        complete: function (jqXHR, textStatus) {
                            doAddEvent4textarea(textareaid, '觸發complete回調函數');
                        }
                    });
            }
            function doAddEvent4textarea(textareaid, txt) {
                var textarea = $("#" + textareaid);
                textarea.val(textarea.val() + '\r\n' + txt);
            }

        效果圖:

           image

5)   $.ajax()方法的全局事件典型用例

你的頁面存在多個甚至為數不少的ajax請求,但是這些ajax請求都有相同的消息機制。ajax請求開始前顯示一個提示框,提示“正在讀取數據”;ajax請求成功時提示框顯示“數據獲取成功”;ajax請求結束后隱藏提示框。

a)   不使用全局事件的做法是:

$.ajax()加上beforeSendsuccesscomplete回調函數,在回調函數中加上處理提示框。

b)   使用全局事件的做法是:

$(document).ajaxStart(onStart)
              	   .ajaxComplete(onComplete)
               	   .ajaxSuccess(onSuccess);
function onStart(event) { //..... }
function onComplete(event, xhr, settings) { //..... }
function onSuccess(event, xhr, settings) { //..... }

3.   jQuery ajax相關函數

1)   jQuery.ajaxSetup({ })

jQuery.ajax()函數中的所有的參數選項都可以通過jQuery.ajaxSetup()函數來全局設置默認值。

2)   $.ajax()函數的封裝

a)   $("").load(url [, params] [, callback])

請求遠程的HTML文件代碼(dataType: "html"),默認使用 GET 方式,如果傳遞了params參數則使用Post方式。在請求“成功”完成時將responseText屬性值插入至DOM中。但不管請求是否成功完成“在最后”都會執行callback回調函數(即:complete:callback)

b)   jQuery.get(url [, data] [, callback] [, type] )

通過HTTP GET請求載入數據,並在請求成功時執行回調函數(即:success: callback)。

c)   jQuery.getJSON(url [, data] [, callback] )

通過 HTTP GET 請求載入 JSON 數據。相當於: jQuery.get(url, [data],[callback], "json")

可以通過使用JSONP 形式的回調函數來加載其他網域的JSON數據。

d)   jQuery.getScript(url [, callback] )

通過 HTTP GET 請求載入並執行一個 JavaScript 文件。相當於: jQuery.get(url, null, [callback], "script")

可以跨域調用 JavaScript 文件。

e)   jQuery.post(url [, data] [, callback] [, type] )

通過 HTTP POST 請求載入信息,並在請求成功時執行回調函數(即:success: callback)。

3)   對象序列化

a)   jQuery.param(object,traditional)

創建數組或對象的序列化表示,該序列化可在ajax請求時在URL查詢字符串中使用。

序列化過程中會使用encodeURIComponent()函數把字符串作為URI組件進行編碼。

encodeURIComponent() 方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。其他字符(比如:;/?:@&=+$,# 這些用於分隔 URI 組件的標點符號),都是由一個或多個十六進制的轉義序列替換的。

// 在param中會進行如下處理
function( key, value ) {
    // 如果value是函數,則取其函數返回值
    value = jQuery.isFunction( value ) ? value() : value;
    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
};

對於 jQuery 1.4$.param() 方法將會通過深度遞歸的方式序列化對象,以便符合現代化腳本語言的需求,比如 PHPRuby on Rails 等。你可以傳遞traditional = true 或在ajax功能中傳遞包含traditionaloptions參數。

           傳送門:$.param()深度遞歸詳解$.param() 示例

b)   $("").serializeArray()

可以將一個或多個表單元素(比如 input textarea等),或者 form 元素本身的jQuery對象序列化為JSON對象。(非 JSON 字符串。需要使用插件或者第三方庫進行字符串化操作)

特別說明,元素不能被禁用(禁用的元素不會被包括在內),並且元素應當有含有 name 屬性。提交按鈕的值也不會被序列化。文件選擇元素的數據也不會被序列化。

           傳送門:$("").serializeArray() 示例

c)   $("").serialize()

可以將一個或多個表單元素(比如 input textarea等),或者 form 元素本身的jQuery對象序列化為經過URL編碼轉換后的字符串,可直接用在URL查詢字符串中。

       jQuery內部定義:

	serialize: function() {
		return jQuery.param( this.serializeArray() );
	}

傳送門:$("").serialize()示例

 

$.ajax()中常見應用示例

1.   cacheifModified參數

1)   cache參數:GETPOST最重要的區別(傳送門)

語義上,GET是獲取指定URL上的資源,是讀操作,重要的一點是不論對某個資源GET多少次,它的狀態是不會改變的,在這個意義上,我們說GET是安全的(不是被密碼學或者數據保護意義上的安全)。因為GET是安全的,所以GET返回的內容可以被瀏覽器,Cache服務器緩存起來。

POST的語意是對指定資源“追加/添加”數據,所以是不安全的,每次提交的POST,參與的代碼都會認為這個操作會修改操作對象資源的狀態,於是,瀏覽器在你按下F5的時候會跳出確認框,緩存服務器不會緩存POST請求返回內容。

2)   ifModified參數:通過ifModified參數提高請求性能(即:“條件GET”:Last-Modified / If-Modified-SinceETag / If-None-Match

當你請求的資源並不是一層不變的時候,即不能簡單的一直使用客戶端緩存時,你可能通過將cache設置為false來發送請求,這實際上是在url加上時間戳組合成新的url,每次發送新的請求,這明顯加大了服務器的壓力。

對於這種情況,我們可以通過ifModified參數改進緩存方式(即:cacheifModified都設置為true),僅在請求的數據改變時重新獲取。通過這種方式請求的url不會改變,並且每次都會發送到服務器,只是會有檢驗方法驗證是否需要重新獲取數據從而節省帶寬和開銷。

更多ETag描述(優點,解決了Last-Modified無法解決的一些問題,什么場合不應該被使用)

過程如下:

a)   $.ajax()函數的cacheifModified參數同時設置為true

b)   客戶端請求服務端A,在服務端加上Last-Modified/ETag響應體一起返回。

c)   客戶端緩存接收到的Last-Modified/ETag響應體,並在下一次發生請求A時將緩存的Last-Modified/ETag值做為If-Modified-Since/IF-None-Match請求頭一起發給服務器。

d)   服務器接收If-Modified-Since/IF-None-Match后,就根據參數值檢驗自上次客服端請求之后資源是否有改動

                          i.      若還未改動則直接返回響應304和一個空的響應體。

                        ii.      若已改動則重新處理數據,返回最新的請求數據。

e)   這樣,既保證不向客戶端重復發出資源,也保證當服務器有變化時,客戶端能夠得到最新的資源。

這一過程中,我們只需要做:服務器返回Last-Modified/ETag響應頭和在服務端檢驗數據是否失效並采取對應處理方式。其余步驟由jQuery框架的ajax()函數完成。

關鍵代碼如下:   

客服端:

            $('#btn_nowTime_long3').bind('click', null
                 , function () {
        		 $.ajax('AjaxHandler.ashx?func=GetServerTime4Modified',
                         {
                             type: 'get',
                             dataType: 'text',
                             cache: true,
                             ifModified: true,
                             success: function (data) {
                                 if (data)
                                     alert(data);
                             },
                     });
             });

服務端:

    if(!String.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"]))
    {
        if (CheckResourceValidate())  // 檢查資源有效性
        {
            // 如果資源有效,則直接返回304狀態碼,客戶端回去到此狀態碼后會從緩存中取值。
            context.Response.StatusCode = 304;

            return;
        }
    }
    // 請求數據
    GetServerTimeAfter2Second();
    context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(5));
    // 設置Last-Modified響應體
    context.Response.Cache.SetLastModified(DateTime.Now);

2.   跨域請求

JavaScript中,有一個很重要的安全性限制,被稱為Same-Origin Policy”(同源策略)。這一策略對於JavaScript代碼能夠訪問的頁面內容做了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。所謂同源是指,域名(host),協議(protocol),端口(port)相同。   

URL

說明

是否允許通信

能否通過javascript解決

http://www.a.com/a.js

http://www.a.com/b.js

同一域名下

允許

 

http://www.a.com/lab/a.js

http://www.a.com/script/b.js

同一域名下不同文件夾

允許

 

http://www.a.com:8000/a.js

http://www.a.com/b.js

同一域名,不同端口

不允許

http://www.a.com/a.js

https://www.a.com/b.js

同一域名,不同協議(httphttps

不允許

不能

http://www.a.com/a.js

http://70.32.92.74/b.js

域名和域名對應ip

不允許

http://www.cnblogs.com/a.js

http://www.a.com/b.js

http://script.a.com/b.js

http://a.com/b.js

不同域名(host)

不允許

注:一級域名與二級域名之間;不同二級域名之間 都屬於不同域名

1)  $.ajax()為我們提供了兩種解決方案,不過都是只支持get方式,分別是jQueryjQuery.ajax“jsonp”格式和jquery.getScript()(即jQuery.ajax “script”格式)方式。

2)   $.ajax()跨域原理分析

由於javascript的安全限制“同源策略”,所以我們無法使用XMLHttpRequest直接請求別的域名下的資源。不過擁有src屬性和href屬性的<script>\<img>\<iframe><link>\<a>標簽不受同源策略影響。$.ajax()提供的兩種解決方案正是應用了動態創建<script>的方式來實現(即:生成<script>標簽,src引入腳本,然后執行,最后移除<script>標簽)。

3)   jQuery.ajax()jsonpscript方式的異同點:

a)   相同:都走$.ajax() script格式的流程;不會觸發全局事件和局部事件;只支持GET方式(POST請求會自動轉化為GET請求);默認不啟用緩存(cache:false)

b)   不同:jsonp方式可以通過jsonpjsonpCallback參數指定一個特定回調函數。

4)   示例部署說明:

    因為是跨域請求,所以需要在本機部署兩個示例程序以模擬不同域之間的訪問,並且在示例代碼中需要修改“crossUrl”為目的域路徑。

5)   jsonp示例代碼:

客服端:

            // jsonp方式跨域請求(dataType:jsonp)
            $('#btn_cross_req1').bind('click', null
                , function () {
                    $.ajax(crossUrl,
                        {
                            type: 'get',
                            dataType: 'jsonp',
                            jsonp: 'jsonpParamName',
                            jsonpCallback: 'crossCallback',
                            crossDomain: true,
                        });
            });
        function crossCallback(data) {
            alert('jsonp' + data);
        }

        服務端:

    context.Response.ContentType = "text/plain";
    string jsonpCallbackName = reqCollection["jsonpParamName"];
    context.Response.Write(String.Format("{0}('來自域:{1}的相應信息')"
                    , jsonpCallbackName, context.Request.Url.Host));

    分析:

a)   jsonpjsonpCallback參數而改變的url如下。(即默認為:callback=jQuery隨機值,更改為:jsonpParamName=crossCallback

URL:http://192.168.1.100:6567/AjaxHandler.ashx?func=CrossRequest&jsonpParamName=crossCallback&_=1368360234428

b)   服務器端獲取到jsonp回調函數名后,返回一個函數表達式。

6)   XMLHttpRequest Level 2中新增了跨域訪問方式、接收二進制等新功能,詳細請看:XMLHttpRequest2 新技巧

 

最后,再來一張示例截圖吧!!!

示例源碼:觸碰jQueryAJAX異步詳解.rar

image

 

 

 

 

 

本篇博文到此結束,主要介紹內容是使用XMLHttpRequest實現ajax請求和XMLHttpRequest Level 2為我們所帶來的改進,最后重點講解了jQuery中通過$.ajax()方法實現ajax以及各個參數的詳細介紹,並立舉了經典示例說明了:跨域請求、ajax全局事件、ajax局部事件、xhr二進制數據處理、如何高效使用緩存……

做為一個后台工程師,你是否想深入了解一些前台必須的技術呢?是的,我想了解,並且會慢慢把所了解到的技術以博文的方式整理分享給大家。

謝謝大家查閱,如果覺得文章不錯,還請多多幫推薦……

 

 

 

 

文中出現比較多的相關資源鏈接,這里整理下方便大家日后快速找到鏈接:

1)   jQuery相關

        jQueryAPI文檔

        jQuery百度百科(Eg:模塊,歷史版本)

Ajax 技術資源中心(IBM

這是有關 Ajax 編程模型信息的一站式中心,包括很多文檔、教程、論壇、blogwiki 和新聞。任何 Ajax 的新信息都能在這里找到。

jQuery.ajax()中的預過濾器和分發機制函數inspectPrefiltersOrTransports詳解

jQuery的deferred對象詳解

$.param()深度遞歸詳解

2)   XMLHttpRequest Level 2 的新功能相關

        XMLHttpRequest 增強功能

        XMLHttpRequest Level 2 使用指南

        XMLHttpRequest2 新技巧

3)   跨域請求相關

JavaScript跨域總結與解決辦法

總結了5js跨域方式:利用<iframe>標簽和document.domain屬性、動態創建scrip、利用<iframe>標簽和location.hash屬性、window.name實現的跨域數據傳輸、使用HTML5 postMessage利用flash跨域

域名和IP地址及域名解析

xhr注入_百度百科

        說說JSON和JSONP

4)   HTTP相關

HTTP深入淺出 http請求

1)   介紹了一次HTTP通信的7個步驟:建立TCP連接、Web瀏覽器向Web服務器發送請求命令、Web瀏覽器發送請求頭信息、Web服務器應答、Web服務器發送應答頭信息、Web服務器向瀏覽器發送數據、Web服務器關閉TCP連接

2)   介紹HTTP請求格式

        HTTP GET和POST的區別

HTTP 頭部詳解

        HTTP Content-type對照表

        格式:Content-Type: [type]/[subtype]; parameter

        HTTP狀態碼一覽表(HTTP Status Code   

    5)Jquery其他部分

        不定義JQuery插件,不要說會JQuery

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM