建立非同步物件



Ajax這個名詞是由 Jesse James Garrett 提出,在他發表的  Ajax: A New Approach to Web Applications 這篇文章中談到 Google Suggest Google Maps 所使用到的技術,是他們在 Adaptive Path 中稱之為Ajax的新方法:
Google Suggest and Google Maps are two examples of a new approach to web applications that we at Adaptive Path have been calling Ajax. The name is shorthand for Asynchronous JavaScript + XML, and it represents a fundamental shift in what’s possible on the Web.

文中表示,Ajax是非同步JavaScript結合XML的概念,XML是用來交換結構化資料,但事實上,並非不是唯一可用的格式。

Ajax的核心概念為非同步,為何要非同步?傳統表單提交、超鏈結點選,瀏覽器會有預設的處理方式,也就是以同步方式傳送請求,接著等待伺服器回應資料, 然後進行換頁動作,在資料提交期間,使用者只能等待最新的畫面回應,中間若作了其它的頁面操作,瀏覽器可能會放棄原有的請求,就算在資料回應之後,使用者 面對的是全新的一個頁面,即使使用者真正所作的只是會更新畫面中某個區域。

如果可以把請求與回應改為非同步進行,也就是發出請求後,瀏覽器無需苦等伺服器的回應,而可以讓使用者對瀏覽 器中的Web應用程式進行其它的操作,又不會中斷原本的請求,當伺服器終於處理完請求並送出回應,而瀏覽器接收到回應時,再回過頭來呼叫瀏覽器所設定的對應 動作進行處理,方式是可以利用DOM操作更新畫面中的某些區域,那麼就開啟了各種可能的互動模式。

在瀏覽器中使用XMLHttpRequest來建立非同步物件,不過存在著跨瀏覽器差異性,Internet Explorer 6以前的版本,以ActiveXObject實作了這個物件的功能,直到Internet Explorer 7才有XMLHttpRequest的實作,就算是ActiveXObject的實作,也存在著版本差異,最基礎的版本是Microsoft.XMLHTTP,較新的版本有Msxml2.XMLHTTP、Msxml2.XMLHTTP.3.0、Msxml2.XMLHTTP.4.0等,Microsoft.XMLHTTP具有較高的相容性,為了相容性,大部份程式庫通常只嘗試Microsoft.XMLHTTP與Msxml2.XMLHTTP(例如 Prototype),或者只嘗試Microsoft.XMLHTTP(例如 jQuery)。

如果要跨瀏覽器處理非同步物件的建立,基本程式碼可以如下:
var xhr = function() {
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
    }
    else {
        try {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }
        catch(e) {
            throw new Error('XMLHttpRequest not supported');
        }
    }
};

在上面的xhr參考的函式中,偵測是否可使用XMLHttpRequest,如果可以使用就建立,否則嘗試建立ActiveXObject,若亦建立失敗則丟出錯誤。

另外要注意,在Internet Explorer 7中實作的XMLHttpRequest,即使文件來自本地端,也無法對本地端的檔案進行請求,如果你有這個需求,可以改用ActiveXObject物件。

如果建立非同步物件的需求很頻繁,不想每次都偵測XMLHttpRequest是否存在,則可以如下設計:
var xhr = window.XMLHttpRequest &&
            (window.location.protocol !== 'file:' || !window.ActiveXObject) ?

          function() {
                return new XMLHttpRequest();
          } :
          function() {
             try {
                 return new ActiveXObject('Microsoft.XMLHTTP');
             } catch(e) {
                
throw new Error('XMLHttpRequest not supported');
             }
          };

在上面的例子中,xhr最後所參考的函式為:若可以取得XMLHttpRequest,且文件來源並非本地端的話,就建立XMLHttpRequest,若可以取得XMLHttpRequest,但文件來源為本地端的話(針對Internet Explorer 7),或者是XMLHttpRequest不存在的話,則建立ActiveXObject。

你可以如下呼叫建立非同步物件:
var request = xhr();

建立非同步物件雖然存在跨瀏覽器差異性,所幸的是建立之後物件上可用的API差異性就少多了。基本上可用的幾個方法如下:
  • void open(string method, string url[, boolean asynch, string username, string password])
開啟對伺服端的連結;method為請求方式('GET'、'POST'、'HEAD'等);url為伺服端位址,如果是GET的話,可加上請求參數與值;asynch為非同步設定,預設是true,表示使用非同步方式,username、password則視伺服端有無要求驗證而設置。

  • void setRequestHeader(string header, string value)
為HTTP請求設定一個標頭值,在open()之後呼叫,通常在open()的method參數為'POST'時使用。

  • void send(content)
對伺服端傳送請求,open()的method為'GET'時,content設為null,'POST'時,content可放字串、XML、JSON格式的內容,會放在POST本體中發送。

  • void abort()
用來中斷請求。

  • string getAllResponseHeaders()
傳回一個字串,其中包含HTTP請求的所有回應標頭。

  • string getResponseHeader(string header)
傳回一個字串,其中包含指定的回應標頭值。

open()方法的第三個參數通常保留預設置true,偶而若想以同步方式,可以設為false。若想知道目前請求物件狀態,可以在呼叫open()方法之前,對onreadystatechange設置處理器函式。只要有狀態變化,則會呼叫所設置的處理器函式。一個例子如下:
var request = xhr();
request.onreadystatechange = function() {
    if(request.readyState === 4) {
        if(request.state === 200) {
            // 對成功回應作處理
        }
    }
};
request.open('GET', 'data.txt');
request.send(null);

非同步物件的readyState會有0到4的變化,代表各個處理階段:
  • 0 - 還沒呼叫open()
  • 1 - 已呼叫open()
  • 2 - 已呼叫send()
  • 3 - 正在接收回應
  • 4 - 伺服端回應結束

通常只會對readyState為4時作處理,也建議忽略4以外的狀態,因為存在跨瀏覽器差異性。非同步物件的status表示
HTTP回應狀態碼,可以使用statusText取得回應狀態碼代表的文字訊息。

你可以從非同步物件的responseText取得伺服端的回應文字,不過要注意,伺服端回應時若沒有指明字元集(例如Content-Type: text/html; charset=UTF-8之類),responseText預設會使用UTF8字元集來解讀傳回的文字。如果回應是XML,則可以使用responseXML取得剖析後的DOM物件。

以下是一個非同步取得資料的完整流程示範,其中請求的純文件中包括中文,所以先儲存為UTF-8格式,執行的結果是以非同步方式取得文件,並在同一個頁面顯示內容:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
var xhr = window.XMLHttpRequest &&
(window.location.protocol !== 'file:'
|| !window.ActiveXObject) ?
function() {
return new XMLHttpRequest();
} :
function() {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
throw new Error('XMLHttpRequest not supported');
}
};

document.getElementById('req').onclick = function() {
var request = xhr();
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
document.getElementById('table').innerHTML =
request.responseText;
}
}
};
request.open('GET', 'XMLHttpRequest-1.txt');
request.send(null);
};
};
</script>
</head>
<body>
<button id='req'>取得表格</button>
<div id="table"></div>
</body>
</html>

更多的細節的在之後文件再詳述,如果目前你需要更多有關於XMLHttpRequest的文件,可以在這邊查詢: