利用原來的文章: https://diary.tw/archives/274 撰寫了一個更容易測試的 html 範例, 一則以測試同步及非同步, 另一則了解在IE及FF的 AJAX Callback 的差異.
幾件事情要特別小心:
1. browser cache:
在做 AJAX Callback 時, 若是 server side 程式不強迫 client side browser 不 cache 時, IE 會發生 callback response cache 的行為, 這個會讓 AJAX 取得的回應發生問題, 當然有多重方法可以解決, 在 FF 就沒有這種現象. 比較單純而又通用解決方法就是在 callback url 上動手腳, 和一般解決 cache 的方法相同, 這裡利用了 javascript 的時間戳記來將 url 變成每次都不同. 一般的實作方法有兩個, 一個是用 Date.parse(new Date()) 方法, 但時間比較不精確(每次取回的值最後三位皆為 000, IE, FF皆同), 另一則是 (new Date()).getTime() 似乎比較精確, 這兩個方法都是傳回從 January 1, 1970, 00:00:00, local time 開始的 milliseconds. 當然, server side 加不加上 cache control 的 header 就比較不影響了, 因為每次 browser 就會乖乖地來 request.
2. callback handler 的放置位置:
非同步呼叫時, 要給定 callback handler function, 由於是跨 function 的呼叫, 若是要寫 generic 一點, 就得將 XMLHttpRequest 物件變數往上一層放, 讓呼叫時, 非同步處理函數仍能取得 XMLHttpRequest 變數進行回應後的處理. 同步呼叫時就沒這個問題, 不過 browser UI 上的使用者經驗就會比較差一點. 另外非同步呼叫時, 最好能放一個動畫, 讓使用者覺得是在處理中.
3. 非同步呼叫的重覆操作行為:
由於同步呼叫時, 無論 IE 或 FF 全頁面都會進行等待的狀態, 所以使用者不會有重覆操作 UI 的問題, 但是非同步呼叫時, 使用者是可以再次操作 UI 上的任何元件. 這個測試是利用非同步呼叫, 連續按兩次後的狀況. FF 在發生時, 第二次的 send 會發生 exception, 而造成兩次的非同步呼叫都沒有回傳給 handler, 當然, 若是先按非同步再按同步呼叫時, 一樣第二次也有 exception, 而且也都沒有回應. 透過sniffer或ethereal來觀察時, 也發現 server 第一次呼叫並無回應正常的200, 而第二次根本也無發出 request 的現象. (應該是第二次呼叫是會先 cancel 第一次呼叫的 reqeust, 所以 server 也就不 response 了)
再來看看 IE 的表現囉, 重覆執行時(無論是兩次非同步呼叫或是第一次非同步, 第二次同步呼叫, 第二次 always 是可以正常工作的, 當然, 第一次仍舊不會有任何結果(sniffer 或 ethereal 也看不到回應, 而第二次仍舊會發出 request), 這個行為似乎比較理想. 但無論如何, 應該要避免使用者重覆去操作這類行為, 以免造成不必要的問題. (特別為此測試在 server sied 將 client 傳回的 t=? 的內容 response 回來, 以方便確認回傳的資料是回應哪次的呼叫.
4. session 及 cookie 皆共用:
這個部分在 IE 及 FF 皆沒有太大問題, 有了這個特性, 才方便做安全性的檢查, 不致有麻煩的使用者身份確認的問題.
5. http_referer client header問題:
這個也十分有趣, IE 會帶回原 callback 的呼叫頁參考來源 url , 但 FF 並不會做這件事, 當然, 就變成要利用這個 client header 來辨識使用者從哪裡來就比較麻煩了. 不過也可以利用 XMLHttpRequest 的 setRequestHeader 方法來自行設定 referer, 語法如下:
obj.setRequestHeader(‘Referer’, document.location.href);
有可能是 IE 是起 activex object, 而 FF 是起內建的 XMLHttpRequest object 才會行為有所不同.
6. 另外一個奇怪的顯示, FireBug 是一個很不錯的 client side javascript 除錯器(詳見: https://diary.tw/archives/245), 而且也有完成的 AJAX Callback 記錄, 以方便除錯. 但若故意在呼叫 XMLHttpRequest send 方法時, 即使使用 GET 方法, 但 FireBug 的 debug UI 上仍會呈現 POST 的內容, 但實際上並不會傳出 (可由 sniffer 或 ethereal 等軟體觀察), 這是要注意不要被誤導的畫面混淆了. (這個測試很無聊吧!! 明明就是 HTTP GET, 就硬要在 send 中加資料, 不過 IE 和 FF 表現都很正常, 不會理這個 send 中的資料)
附件為一個 ajax.htm 配合 test.php (server side) 的範例, 本文測試皆以這個範例程式為準, FF 為 1.5.0.6 (Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6), IE 為 6.0.2900.2180.xpsp_sp2_gdr.050301-1519 , 若有興趣, 可以自行下載研究, 並歡迎心得分享.
ajax.zip
實測用網址:
http://sample.diary.tw/1/ajax.htm
alice 2006/09/04 17:09
關於重覆操作行為
我發現初始時改寫成這樣就不會有問題了說
if (window.XMLHttpRequest) // if Mozilla, Safari etc
xmlhttp = new XMLHttpRequest()
else if (window.ActiveXObject){ // if IE
try {
xmlhttp = new ActiveXObject(“Msxml2.XMLHTTP”)
}
catch (e){
try{
xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”)
}
catch (e){}
}
}
else
return falseTimothy 2006/09/05 00:33
重新調整了一下 initial 的結構, 發現確實有效:
// initial xmlhttp start..
if (window.ActiveXObject){ // if IE
try{
xmlhttp = new ActiveXObject(“Msxml2.XMLHTTP”);
}
catch(e){
try{
xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”);
}
catch(e){ xmlhttp = false; }
}
}
else{
if (window.XMLHttpRequest){ // if Mozilla, Safari etc
try{
xmlhttp = new XMLHttpRequest();
}
catch (e){ xmlhttp = false; }
}
else{
if (window.createRequest){
try {
xmlhttp = window.createRequest();
} catch (e) { xmlhttp=false; }
}
}
}if(!xmlhttp) return false;
// initial xmlhttp end..
於是再做一個 sample 來進行測試:
http://sample.diary.tw/1/ajax2.htm真的在 FF 下就不會有 exception 發生了, 更好玩的是, 多次操作在 FireBug 下的 response 都會有回應, 但是在顯示的時候仍只有第二次呼的回傳結果. 推測應該是 handler 第二次被設定掉的關係(和第一次的 instance 不同).