JavaScript - Asynchronous JavaScript And XML, AJAX

同步與非同步

同步HTTP請求:請求 → 顯示 → 互動 → 暫停 → 請求 → 顯示 → 互動 → 暫停 …

需要不斷重新刷新整個頁面,會出現一段時間的空白。

非同步HTTP請求:請求 → 顯示 → 互動 → (請求)互動 → (請求)互動 …

非同步HTTP請求在顯示、執行互動不需要再暫停操作,可以馬上執行所需的互動,只向伺服器要求一小部份資料,然後更新頁面的一小部份, 不需要重新刷新整個頁面,也不會出現一段時間的空白。

非同步好處

  1. 減輕伺服器的負擔,加快瀏覽速度(只給所需)
  2. 帶來更好的用戶體驗(更流暢的畫面)
  3. 標準化技術,主流瀏覽器都支援
  4. 促進頁面呈現與資料分離(提高可維護性)

AJAX組成

AJAX不是單一技術,是集4種技術而成,JavaScript、CSS、DOM、XMLHttpRequest。(或說5種,再加上(X)HTML)

JavaScript AJAX技術組成
名稱說明
JavaScript通用指令碼語言。是撰寫AJAX主要程式語言
CSS為Web頁面元素提供視覺化樣式的定義方法, AJAX透過修改CSS樣式,改變使用者介面
DOMAJAX可以在執行時透過DOM來改變使用者介面,或局部更新某個節點
XMLHttpRequestXMLHttpRequest物件允許Web開發人員向伺服器以非同步方式取資料, 資料格式通常是XML或是文字,目前較流行為JSON

XMLHttpRequest物件

能夠直接在Client使用Javascript來提出HTTP請求。IE內建MSXML剖析器有多個版本,其提供的XMLHttpReqest物件也有多個版本(不建議使用)。

KKBruce提醒,除非您必須處理類似IE等相容性問題,不然是不建議使用以下表格內容。

JavaScript IE內建MSXML剖析器
版本DLL名稱ProgID
2.0(IE5)msxml.dllMicrosoft.XMLHttp
2.6msxml2.dllMSXML2.XMLHttp
3.0(IE6)msxml3.dllMSXML2.XMLHttp.3.0
4.0msxml4.dllMSXML2.XMLHttp.4.0
5.0msxml5.dllMSXML2.XMLHttp.5.0
6.0msxml6.dllMSXML2.XMLHttp.6.0

建立XMLHttpRequest物件

在Mozilla,Chrome和Safaris等主流瀏覽器是內建物件,例如,

// 1. 透過 new XMLHttpRequest() 產生物件
// 2. 注意大小寫
var xmlHttp=new XMLHttpRequest();

在IE6之前是透過ActiveX控制項物件(由MSXML提供),例如,

var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");

IE7之後,同時支援原生XMLHttpRequest物件(預設值)和ActiveX控制項物件,例如,

var xmlHttp;
function createXMLHttpRequest(){
  if (window.XMLHttpRequest) //支援原生型
    xmlHttp = new XMLHttpRequest();
  else if (window.ActiveXObject) //IE6以下
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}

[KKBruce補充] 建立XMLHttpRequest物件注意事項

XMLHttpRequest物件屬性

JavaScript XMLHttpRequest物件屬性
名稱說明
readyState唯讀屬性,目前狀態
responseBody唯讀屬性,取得伺服器的回應,傳回非符號位元組的陣列
responseSteam唯讀屬性,取得伺服器的回應,傳回IStream資料流物件
responseText唯讀屬性,取得伺服器的回應,傳回字串
responseXML唯讀屬性,取得伺服器的回應,傳回XML DOM物件,可作DOM處理
status唯讀屬性,取得HTTP狀態碼
statusText唯讀屬性,取得HTTP狀態訊息字串
onreadystatechange存取屬性,執行readyState屬性改變的事件處理,設定處理的程序

readyState會回傳一數值,以表示目前處理的進度。

JavaScript readyState回傳狀態值
名稱說明
0uninitalized,未初始化
1loading,載入中
2loaded,載入完成
3interactive,可互動
4completed,完成

status會回傳狀態碼,以表示目前處理的結果。

流程 -
名稱說明
200OK,請求成功
202請求被接收,但處理未完成
400錯誤的請求
404Not Found,找不到網頁
500Internal Server Error,內部錯誤
...…其他網頁錯誤碼

[KKBruce補充] 這裡有一篇整理很棒的 HTTP 狀態碼,【HTTP 狀態碼,在 IIS 7.0 和 IIS 7.5】非常建議加入我的最愛中,在查詢錯誤的 HTTP 狀態碼時非常有用。例如,我們一般只知道 404 狀態碼是找不到,但 404 下還有整整 20 項細部的定義,這對於開發人員是不可多得的資訊,可以更快速清楚問題所在,而不是用經驗法則在處理。

XMLHttpRequest物件方法

JavaScript XMLHttpRequest物件方法
名稱說明
abort()取消目前HTTP請求
getAllResponseHeaders()取得全部HTTP標頭內容
getResponseHeader("指定Header名稱")取得指定HTTP標頭名稱的內容
open("GET|POST","URL",async,userid,pwd)開啟HTTP請求
send("參數")傳送HTTP請求到伺服器
setRequestHeader("自訂Header名稱",value)自訂HTTP標頭資料

XMLHttpRequest物件open方法參數
名稱說明
GET取得伺服器回應
POST傳送資料到伺服器
URL請求的XML文件或伺服端程式(ASP.NET, PHP, JSP...)
async布林值, 預設true,false表示需要等到伺服器回應後才繼續執行Javascript
userid, pwd伺服器需要id/password,才需要使用

send("參數")說明

參數表示要向伺服器發送的資料,參數格式為查詢字串的形式,例如,

var param = "name=Bruce&age=18";
在open中指定為GET,則參數作為查詢字串傳送,如果指定為POST,則作為HTTP的POST方法傳送,對於send()方法而言,此參數為必需, 如不發送任何資料,請傳送null即可。

// GET
xmlHttp.send(null);
// POST
xmlHttp.send(param);

setRequestHeader()說明

如果使用POST方法,在發送之前必須設定HTTP標頭,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 加上UTF-8是最好
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

AJAX下的GET與POST

function startRequest(){
    // 建立xmlHttp物件
 createXMLHttpRequest(); 
 // 向Demo.aspx,使用GET開啟請求
 xmlHttp.open("GET","Demo.aspx");
 // 狀態有改變時,就執行
 xmlHttp.onreadystatechange = function(){
     // 當狀態值為4(完成)與狀態碼為200(請求成功)
  if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
      // Do something
   alert("服務器傳回訊息: " + xmlHttp.responseText);
 }
 // 傳送HTTP請求到伺服器
 // GET記得加上null
 xmlHttp.send(null);
}

IE會自動暫存非同步的結果,解決方法是每次非同步請求的URL不同,我們可以加上一個伺服器不需要的參數,讓每次的URL都不同。

// 加上一個時間戳計當亂數使用
var url = "Demo.aspx?timestamp=" & new Date().getTime();
xmlHttp.open("GET",url, true);

GET請求如果有參數,直接放入URL網址中,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?" + queryString + "&timestamp=" + new Date().getTime();

POST請求有參數,統一放在send()中來傳送,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?timestamp=" & new Date().getTime();
// url放在open()
xmlHttp.open("POST",url,true);
...
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
// queryString放在send()
xmlHttp.send(queryString);

POST方法中文會出現亂碼

在IE8與Firefox上測試已無此問題,但為了相容性還是建議加上解決方法。

這是因為XMLHttpRequest物件在處理傳回responseText/XML時是使用UTF-8編碼,建議使用解決方法2,對回傳的資料進行decodeURIComponent()解碼即可。

解決方法一

對要傳送的資料進行兩次encodeURI()編碼,以解決中文亂碼問題,例如,

...
xmlHttp.send(encodeURI(encodeURI(queryString)));

對傳回資料responseText/XML再進行一次decodeURI()解碼,例如,

responseDiv.innerHTML = decodeURI(xmlHttp.responseText);

解決方法二

setRequestHeader直接宣告(charset)使用UTF-8,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
xmlHttp.send(queryString);

對傳回資料responseText/XML進行decodeURIComponent()解碼,例如,

responseDiv.innerHTML = decodeURIComponent((xmlHttp.responseXML));

解決方法三

ASP,ASP.NET: 輸出之前Response.Charset = "big5 | gb2312",依取得的頁面charset設定。

onreadystatechange方法

AJAX的重點在狀態改變後的事件處理函式(handler),也可以說,這是唯一我們必須自訂(手動)設計程式碼的地方,換個方向說,我們在請求/回應,而伺服器回應資料後,我們要如何處理及顯示到網頁上(DOM)。

同時觸發非同步問題

如果我們必須同時觸發(呼叫)多個AJAX物件,因為我們通常都是把建立的AJAX物件的xmlHttp宣告為全域變數,所以可能會產生覆蓋的情況。

解決同時觸發非同步問題

就是將xmlHttp宣告為區域變數來處理,並在收到伺服器資料且處理完後,手動刪除。

function getData(){
  var xmlHttp="";
  createXMLHttpRequest();
  xmlHttp.onreadystatechange = function(){
    ...
    // 處理完立即刪除與釋放
    delete xmlHttp;
    xmlHttp = null;
  }
}

delete關鍵字

刪除物件的property。The delete operator deletes an object, an object's property, or an element at a specified index in an array.

2 則留言:

  1. 可否請教一下,在 IE9/Chrome 下 responseText 有傳回字串,但在 iPhone / Android 下 readyState=4 及 status = 200 都正確, 就是字串是零字串(Empty), 是哪裡錯了呢? 謝謝!

    回覆刪除
  2. 因看不到你的程式,建議可以做以下處理:
    1. 使用 JSON 格式來傳遞 Client <--> Server 的物件資料。
    2. 使用 iPhone / Android 模擬器進行除錯。

    回覆刪除

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。