XMLHTTP.readyState的五種狀態


對於很多 Web 開發人員來說,只需要生成簡單的請求並接收簡單的響應即可;但是對於希望掌握 Ajax 的開發人員來說,必須要全面理解 HTTP 狀態代碼、就緒狀態和 XMLHttpRequest 對象。在本文中,Brett McLaughlin 將向您介紹各種狀態代碼,並展示瀏覽器如何對其進行處理,本文還給出了在 Ajax 中使用的比較少見的 HTTP 請求。

在本系列的 上篇文章 中,我們將詳細介紹 XMLHttpRequest 對象,它是 Ajax 應用程序的中心,負責處理服務器端應用程序和腳本的請求,並處理從服務器端組件返回的數據。由於所有的 Ajax 應用程序都要使用 XMLHttpRequest 對象,因此您可能會希望熟悉這個對象,從而能夠讓 Ajax 執行得更好。

在本文中,我將在上一篇文章的基礎上重點介紹這個請求對象的 3 個關鍵部分的內容:

  • HTTP 就緒狀態
  • HTTP 狀態代碼
  • 可以生成的請求類型

這三部分內容都是在構造一個請求時所要考慮的因素;但是介紹這些主題的內容太少了。然而,如果您不僅僅是想了解 Ajax 編程的常識,而是希望了解更多內容,就需要熟悉就緒狀態、狀態代碼和請求本身的內容。當應用程序出現問題時 —— 這種問題總是存在 —— 那么如果能夠正確理解就緒狀態、如何生成一個 HEAD 請求或者 400 的狀態代碼的確切含義,就可以在 5 分鍾內調試出問題,而不是在各種挫折和困惑中度過 5 個小時。

XMLHttpRequest 或 XMLHttp:換名玫瑰

Microsoft™ 和 Internet Explorer 使用了一個名為 XMLHttp 的對象,而不是 XMLHttpRequest 對象,而 Mozilla、Opera、Safari 和 大部分非 Microsoft 瀏覽器都使用的是后者。為了簡單性起見,我將這兩個對象都簡單地稱為 XMLHttpRequest。這既符合我們在 Web 上看到的情況,又符合 Microsoft 在 Internet Explorer 7.0 中使用 XMLHttpRequest 作為請求對象的意圖。(有關這個問題的更多內容,請參見 第 2 部分。)

下面讓我們首先來看一下 HTTP 就緒狀態。

深入了解 HTTP 就緒狀態

您應該還記得在上一篇文章中 XMLHttpRequest 對象有一個名為 readyState 的屬性。這個屬性確保服務器已經完成了一個請求,通常會使用一個回調函數從服務器中讀出數據來更新 Web 表單或頁面的內容。清單 1 給出了一個簡單的例子(這也是本系列的上一篇文章中的一個例子 —— 請參見 參考資料)。


清單 1. 在回調函數中處理服務器的響應


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
} else
alert("status is " + request.status);
}
}

 

這顯然是就緒狀態最常見(也是最簡單)的用法。正如您從數字 "4" 中可以看出的一樣,還有其他幾個就緒狀態(您在上一篇文章中也看到過這個清單 —— 請參見 參考資料):

  • 0:請求未初始化(還沒有調用 open())。
  • 1:請求已經建立,但是還沒有發送(還沒有調用 send())。
  • 2:請求已發送,正在處理中(通常現在可以從響應中獲取內容頭)。
  • 3:請求在處理中;通常響應中已有部分數據可用了,但是服務器還沒有完成響應的生成。
  • 4:響應已完成;您可以獲取並使用服務器的響應了。

如果您希望不僅僅是了解 Ajax 編程的基本知識,那么就不但需要知道這些狀態,了解這些狀態是何時出現的,以及如何來使用這些狀態。首先,您需要學習在每種就緒狀態下可能碰到的是哪種請求狀態。不幸的是,這一點並不直觀,而且會涉及幾種特殊的情況。

隱秘就緒狀態

第一種就緒狀態的特點是 readyState 屬性為 0(readyState == 0),表示未初始化狀態。一旦對請求對象調用 open() 之后,這個屬性就被設置為 1。由於您通常都是在一對請求進行初始化之后就立即調用 open(),因此很少會看到 readyState == 0 的狀態。另外,未初始化的就緒狀態在實際的應用程序中是沒有真正的用處的。

不過為了滿足我們的興趣,請參見 清單 2 的內容,其中顯示了如何在 readyState 被設置為 0 時來獲取這種就緒狀態。


清單 2. 獲取 0 就緒狀態


function getSalesData() {
// Create a request object
createRequest();
alert("Ready state is: " + request.readyState);

// Setup (initialize) the request
var url = "/boards/servlet/UpdateBoardSales";
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}

 

在這個簡單的例子中,getSalesData() 是 Web 頁面調用來啟動請求(例如點擊一個按鈕時)所使用的函數。注意您必須在調用 open()之前 來查看就緒狀態。圖 1 給出了運行這個應用程序的結果。


圖 1. 就緒狀態 0
就緒狀態 0

當 0 等於 4 時

在多個 JavaScript 函數都使用相同的請求對象時,您需要檢查就緒狀態 0 來確保這個請求對象沒有正在使用,這種機制會產生問題。由於 readyState == 4 表示一個已完成的請求,因此您經常會發現那些目前沒在使用的處於就緒狀態的請求對象仍然被設置成了 4 —— 這是因為從服務器返回來的數據已經使用過了,但是從它們被設置為就緒狀態之后就沒有進行任何變化。有一個函數 abort() 會重新設置請求對象,但是這個函數卻不是真正為了這個目的而使用的。如果您 必須 使用多個函數,最好是為每個函數都創建並使用一個函數,而不是在多個函數之間共享相同的對象。

顯然,這並不能為您帶來多少好處;需要確保 尚未 調用 open() 函數的情況很少。在大部分 Ajax 編程的真實情況中,這種就緒狀態的唯一用法就是使用相同的 XMLHttpRequest 對象在多個函數之間生成多個請求。在這種(不常見的)情況中,您可能會在生成新請求之前希望確保請求對象是處於未初始化狀態(readyState == 0)。這實際上是要確保另外一個函數沒有同時使用這個對象。

查看正在處理的請求的就緒狀態

除了 0 就緒狀態之外,請求對象還需要依次經歷典型的請求和響應的其他幾種就緒狀態,最后才以就緒狀態 4 的形式結束。這就是為什么您在大部分回調函數中都可以看到 if (request.readyState == 4) 這行代碼;它確保服務器已經完成對請求的處理,現在可以安全地更新 Web 頁面或根據從服務器返回來的數據來進行操作了。

要查看這種狀態發生的過程非常簡單。如果就緒狀態為 4,我們不僅要運行回調函數中的代碼,而且還要在每次調用回調函數時都輸出就緒狀態。 清單 3給出了一個實現這種功能的例子。


清單 3. 查看就緒狀態


function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState);
}

 

如果您不確定如何運行這個函數,就需要創建一個函數,然后在 Web 頁面中調用這個函數,並讓它向服務器端的組件發送一個請求(例如 清單 2 給出的函數,或本系列文章的第 1 部分和第 2 部分中給出的例子)。確保在建立請求時,將回調函數設置為 updatePage();要實現這種設置,可以將請求對象的 onreadystatechange 屬性設置為 updatePage()

這段代碼就是 onreadystatechange 意義的一個確切展示 —— 每次請求的就緒狀態發生變化時,就調用 updatePage(),然后我們就可以看到一個警告了。圖 2 給出了一個調用這個函數的例子,其中就緒狀態為 1。


圖 2. 就緒狀態 1
就緒狀態 1

您可以自己嘗試運行這段代碼。將其放入 Web 頁面中,然后激活事件處理程序(單擊按鈕,在域之間按 tab 鍵切換焦點,或者使用設置的任何方法來觸發請求)。這個回調函數會運行多次 —— 每次就緒狀態都會改變 —— 您可以看到每個就緒狀態的警告。這是跟蹤請求所經歷的各個階段的最好方法。

瀏覽器的不一致性

在對這個過程有一個基本的了解之后,請試着從幾個不同的瀏覽器中訪問您的頁面。您應該會注意到各個瀏覽器如何處理這些就緒狀態並不一致。例如,在 Firefox 1.5 中,您會看到以下就緒狀態:

  • 1
  • 2
  • 3
  • 4

這並不奇怪,因為每個請求狀態都在這里表示出來了。然而,如果您使用 Safari 來訪問相同的應用程序,就應該看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的狀態:

  • 2
  • 3
  • 4

Safari 實際上把第一個就緒狀態給丟棄了,也並沒有什么明顯的原因說明為什么要這樣做;不過這就是 Safari 的工作方式。這還說明了一個重要的問題:盡管在使用服務器上的數據之前確保請求的狀態為 4 是一個好主意,但是依賴於每個過渡期就緒狀態編寫的代碼的確會在不同的瀏覽器上得到不同的結果。

例如,在使用 Opera 8.5 時,所顯示的就緒狀態情況就更加糟糕了:

  • 3
  • 4

最后,Internet Explorer 會顯示如下狀態:

  • 1
  • 2
  • 3
  • 4

如果您碰到請求方面的問題,這就是用來發現問題的 首要之處。最好的方式是在 Internet Explorer 和 Firefox 都進行一下測試 —— 您會看到所有這 4 種狀態,並可以檢查請求的每個狀態所處的情況。

接下來我們再來看一下響應端的情況。

顯微鏡下的響應數據

一旦我們理解在請求過程中發生的各個就緒狀態之后,接下來就可以來看一下 XMLHttpRequest 對象的另外一個方面了 —— responseText 屬性。回想一下在上一篇文章中我們介紹過的內容,就可以知道這個屬性用來從服務器上獲取數據。一旦服務器完成對請求的處理之后,就可以將響應請求數據所需要的任何數據放到請求的 responseText 中了。然后回調函數就可以使用這些數據,如 清單 1 和 清單 4 所示。


清單 4. 使用服務器上返回的響應


function updatePage() {
if (request.readyState == 4) {
var newTotal = request.responseText;
var totalSoldEl = document.getElementById("total-sold");
var netProfitEl = document.getElementById("net-profit");
replaceText(totalSoldEl, newTotal);

/* 圖 out the new net profit */
var boardCostEl = document.getElementById("board-cost");
var boardCost = getText(boardCostEl);
var manCostEl = document.getElementById("man-cost");
var manCost = getText(manCostEl);
var profitPerBoard = boardCost - manCost;
var netProfit = profitPerBoard * newTotal;

/* Update the net profit on the sales form */
netProfit = Math.round(netProfit * 100) / 100;
replaceText(netProfitEl, netProfit);
}

 

清單 1 相當簡單;清單 4 稍微有點復雜,但是它們在開始時都要檢查就緒狀態,並獲取 responseText 屬性的值。

查看請求的響應文本

與就緒狀態類似,responseText 屬性的值在整個請求的生命周期中也會發生變化。要查看這種變化,請使用如 清單 5 所示的代碼來測試請求的響應文本,以及它們的就緒狀態。


清單 5. 測試 responseText 屬性


function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState +
" and a response text of '" + request.responseText + "'");
}

 

現在在瀏覽器中打開 Web 應用程序,並激活您的請求。要更好地看到這段代碼的效果,請使用 Firefox 或 Internet Explorer,因為這兩個瀏覽器都可以報告出請求過程中所有可能的就緒狀態。例如在就緒狀態 2 中,就沒有定義 responseText (請參見 圖 3);如果 JavaScript 控制台也已經打開了,您就會看到一個錯誤。


圖 3. 就緒狀態為 2 的響應文本
就緒狀態為 2 的響應文本

不過在就緒狀態 3 中,服務器已經在 responseText 屬性中放上了一個值,至少在這個例子中是這樣(請參見 圖 4)。


圖 4. 就緒狀態為 3 的響應文本
就緒狀態為 3 的響應文本

您會看到就緒狀態為 3 的響應在每個腳本、每個服務器甚至每個瀏覽器上都是不一樣的。不過,這在調試應用程序中依然是非常有用的。

獲取安全數據

所有的文檔和規范都強調,只有在就緒狀態為 4 時數據才可以安全使用。相信我,當就緒狀態為 3 時,您很少能找到無法從 responseText 屬性獲取數據的情況。然而,在應用程序中將自己的邏輯依賴於就緒狀態 3 可不是什么好主意 —— 一旦您編寫了依賴於就緒狀態 3 的完整數據的的代碼,幾乎就要自己來負責當時的數據不完整問題了。

比較好的做法是向用戶提供一些反饋,說明在處於就緒狀態 3 時,很快就會有響應了。盡管使用 alert() 之類的函數顯然不是什么好主意 —— 使用 Ajax 然后使用一個警告對話框來阻塞用戶顯然是錯誤的 —— 不過您可以在就緒狀態發生變化時更新表單或頁面中的域。例如,對於就緒狀態 1 來說要將進度指示器的寬度設置為 25%,對於就緒狀態 2 來說要將進度指示器的寬度設置為 50%,對於就緒狀態 3 來說要將進度指示器的寬度設置為 75%,當就緒狀態為 4 時將進度指示器的寬度設置為 100%(完成)。

當然,正如您已經看到的一樣,這種方法非常聰明,但它是依賴於瀏覽器的。在 Opera 上,您永遠都不會看到前兩個就緒狀態,而在 Safari 上則沒有第一個(1)。由於這個原因,我將這段代碼留作練習,而沒有在本文中包括進來。

現在應該來看一下狀態代碼了。

 

轉載自:http://hi.baidu.com/%D0%C2%D4%C2%D3%EA%B9%E2/blog/item/2e30469bbdcb72bdc9eaf499.html


免責聲明!

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



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