如果我們在用戶端頻繁使用ajax技術,那麼我們就不得不多次建立xmlhttp對象。當然,如您所知,我們可以改進建立的方式,比如使用全域變數來緩衝一個執行個體(用戶端的單例模式?!),對於同步方式的通訊,這是很有效,但是這樣的方式對於非同步通訊會出現問題,因為沒有了進程的堵塞,使用者可能在上一次通訊未完成時再次調用同一個xmlhttp執行個體,這樣不等前一個調用的回呼函數觸發,前一次調用就被“覆蓋”掉了(也就代表前一次調用失敗)。建立一個保持xmlhttp執行個體的池,好處顯而易見,最明顯的優點就是我們不會建立冗餘對象,同時也不會出現在同一個正在被調用的xmlhttp執行個體上出現再次被操作的情況。
具體實現思路:
我們使用一個數組來儲存已建立的xmlhttp對象執行個體,然後每次調用從池中去取一個執行個體。xmlhttp執行個體通訊完畢後我們不用做任何處置,因為它自身的readyState屬性可以標識出它是否可用,如果當時沒有閒置xmlhttp執行個體,且池中的執行個體數小於最大執行個體個數,那麼就建立一個新的執行個體並放入池中。重新改進的實現代碼如下: 複製代碼 代碼如下://封裝XMLHTTP的MyAjaxObj類
var MyAjaxObj = new Object();
var maxXmlHttpCount = 5; //最多5個xmlhttp對象存在
MyAjaxObj.reqList = []; //可以清空裡面的項
MyAjaxObj.getFreeObj = function() {
var req = null;
var len = this.reqList.length;
//先從當前的池裡取
for (var i = 0; i < len; i++) {
if (this.reqList[i]) {
if (this.reqList[i].readyState == 4 || this.reqList[i].readyState == 0) {
req = this.reqList[i];
break;
}
}
}
//如果沒有閑置的對象,自己獨立建立
if (req == null) {
if (this.reqList.length < maxXmlHttpCount) {
req = getXmlHttp();
this.reqList.push(req);
}
}
return req;
}
//建立一個XMLHTTP對象,相容不同的瀏覽器
function getXmlHttp() {
var xmlHttp = false;
var arrSignatures = ["MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP",
"Microsoft.XMLHTTP"];
for (var i = 0; i < arrSignatures.length; i++) {
try {
xmlHttp = new ActiveXObject(arrSignatures[i]);
return xmlHttp;
}
catch (oError) {
xmlHttp = false; //ignore
}
}
// throw new Error("MSXML is not installed on your system.");
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
return xmlHttp;
}
/*封裝XMLHTTP向伺服器發送請求的操作
url:向伺服器請求的路徑;method:請求的方法,即是get/post;***callback:當伺服器成功返回結果時,調用的函數(類似c#回呼函數)***
data:向伺服器請求時附帶的資料;urlencoded:url是否編碼;cached:是否使用緩衝; callBackError;當伺服器返回錯誤時調用的函數
*/
MyAjaxObj.send = function(url, method, callback, data, urlencoded, cached, callBackError) {
var req = this.getFreeObj(); //從池裡或者直接執行個體化一個XMLHTTP的執行個體
//當XMLHTTP的請求狀態發生改變時調用 (核心處理函數)
req.onreadystatechange = function() {
// 當請求已經載入
if (req.readyState == 4) {
// 當請求返回成功
if (req.status == 200) { //或者 req.status < 400
// 當定義了成功回呼函數時,執行成功回呼函數
if (callback)
callback(req, data);
}
// 當請求返回錯誤
else {
//當定義了失敗回呼函數時,執行失敗回呼函數
if (callBackError)
callBackError(req, data);
}
// 有池的管理,我們可以省卻釋放資源的方法
// try {
// delete req;
// req = null;
// }
// catch (e) {
// alert(e.message);
// }
}
}
//如果以POST方式回傳伺服器
if (method.toUpperCase() == "POST") {
req.open("POST", url, true);
//請求是否需要緩衝(只有在req.open之後才可以設定此項)
if (cached)
req.setRequestHeader("If-Modified-Since", "0");
//請求需要編碼
if (urlencoded)
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send(data);
MyAjaxObj.reqList.push(req);
}
//以GET方式請求
else {
req.open("GET", url, true);
//請求是否需要緩衝
if (cached)
req.setRequestHeader("If-Modified-Since", "0");
req.send(null);
MyAjaxObj.reqList.push(req);
}
return req;
}
//全部清除XMLHTTP數組元素,釋放資源
MyAjaxObj.clearReqList = function() {
var len = MyAjaxObj.reqList.length;
for (var i = 0; i < len; i++) {
var req = MyAjaxObj.reqList[i];
if (req) {
try {
delete req;
} catch (e) { }
}
}
MyAjaxObj.reqList = [];
}
//進一步封裝XMLHTTP以POST方式發送請求時的代碼
//isClear:是否清除XMLHTTP數組的所有元素;其他參數的意義見 MyAjaxObj.send
MyAjaxObj.sendPost = function(url, data, callback, isClear, isCached, callBackError) {
if (isClear) {
MyAjaxObj.clearReqList();
}
MyAjaxObj.send(url, "POST", callback, data, true, isCached, callBackError); //post方法需要編碼
}
//進一步封裝XMLHTTP以GET方式發送請求時的代碼
MyAjaxObj.sendGet = function(url, args, callback, isClear, isCached, callBackError) {
if (isClear)
MyAjaxObj.clearReqList();
return MyAjaxObj.send(url, "GET", callback, args, false, isCached, callBackError);
}
最後再ps:上周周末和一個哥們聊天的時候談到ajax應用中的xmlhttp對象。那哥們ms很“虔誠”地問我說xmlhttp怎麼就非同步通訊了。我當時竟然毫不思索地說因為這個對象處理我們的請求調用是“非同步”的(當然可以設定成同步的,不過這是一句廢話),當前這個請求不會影響其他的操作。這個回答是很“官方”的,顯然沒有說到問題的本質。哥們,您的眼神兒有必要那麼bs人嗎?現在稍作分析,個人認為其實每個xmlhttp非同步請求都會觸發一個回呼函數,這個回呼函數的調用不影響其他的操作,這個方法才是“非同步”。如果對比c#裡的非同步處理回調方法,它們在原理上其實是相通的。 哈哈,現在終於想通了, 真是太驕傲,太有出息了,想到就興奮!