在ajax应用中,通常一个页面要同时发送多个请 求,如果只有一个XMLHttpRequest对象,前面的请求还未完成,后面的就会把前面的覆盖掉,如果每次都创建一个新的 XMLHttpRequest对象,也会造成浪费。解决的办法就是创建一个XMLHttpRequset的对象池,如果池里有空闲的对象,则使用此对象, 否则将创建一个新的对象。
下面是我最近写的一个简单的类:
/**
* XMLHttpRequest Object Pool
*
* @author legend <legendsky@hotmail.com>
* @link http://www.ugia.cn/?p=85
* @Copyright www.ugia.cn
*/
var XMLHttp = {
_objPool: [],
_getInstance: function ()
{
for (var i = 0; i < this._objPool.length; i ++)
{
if (this._objPool[i].readyState == 0 || this._objPool[i].readyState == 4)
{
return this._objPool[i];
}
}
// IE5中不支持push方法
this._objPool[this._objPool.length] = this._createObj();
return this._objPool[this._objPool.length - 1];
},
_createObj: function ()
{
if (window.XMLHttpRequest)
{
var objXMLHttp = new XMLHttpRequest();
}
else
{
var MSXML = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
for(var n = 0; n < MSXML.length; n ++)
{
try
{
var objXMLHttp = new ActiveXObject(MSXML[n]);
break;
}
catch(e)
{
}
}
}
// mozilla某些版本没有readyState属性
if (objXMLHttp.readyState == null)
{
objXMLHttp.readyState = 0;
objXMLHttp.addEventListener("load", function ()
{
objXMLHttp.readyState = 4;
if (typeof objXMLHttp.onreadystatechange == "function")
{
objXMLHttp.onreadystatechange();
}
}, false);
}
return objXMLHttp;
},
// 发送请求(方法[post,get], 地址, 数据, 回调函数)
sendReq: function (method, url, data, callback)
{
var objXMLHttp = this._getInstance();
with(objXMLHttp)
{
try
{
// 加随机数防止缓存
if (url.indexOf("?") > 0)
{
url += "&randnum=" + Math.random();
}
else
{
url += "?randnum=" + Math.random();
}
open(method, url, true);
// 设定请求编码方式
setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
send(data);
onreadystatechange = function ()
{
if (objXMLHttp.readyState == 4 && (objXMLHttp.status == 200 || objXMLHttp.status == 304))
{
callback(objXMLHttp);
}
}
}
catch(e)
{
alert(e);
}
}
}
};
* XMLHttpRequest Object Pool
*
* @author legend <legendsky@hotmail.com>
* @link http://www.ugia.cn/?p=85
* @Copyright www.ugia.cn
*/
var XMLHttp = {
_objPool: [],
_getInstance: function ()
{
for (var i = 0; i < this._objPool.length; i ++)
{
if (this._objPool[i].readyState == 0 || this._objPool[i].readyState == 4)
{
return this._objPool[i];
}
}
// IE5中不支持push方法
this._objPool[this._objPool.length] = this._createObj();
return this._objPool[this._objPool.length - 1];
},
_createObj: function ()
{
if (window.XMLHttpRequest)
{
var objXMLHttp = new XMLHttpRequest();
}
else
{
var MSXML = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
for(var n = 0; n < MSXML.length; n ++)
{
try
{
var objXMLHttp = new ActiveXObject(MSXML[n]);
break;
}
catch(e)
{
}
}
}
// mozilla某些版本没有readyState属性
if (objXMLHttp.readyState == null)
{
objXMLHttp.readyState = 0;
objXMLHttp.addEventListener("load", function ()
{
objXMLHttp.readyState = 4;
if (typeof objXMLHttp.onreadystatechange == "function")
{
objXMLHttp.onreadystatechange();
}
}, false);
}
return objXMLHttp;
},
// 发送请求(方法[post,get], 地址, 数据, 回调函数)
sendReq: function (method, url, data, callback)
{
var objXMLHttp = this._getInstance();
with(objXMLHttp)
{
try
{
// 加随机数防止缓存
if (url.indexOf("?") > 0)
{
url += "&randnum=" + Math.random();
}
else
{
url += "?randnum=" + Math.random();
}
open(method, url, true);
// 设定请求编码方式
setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
send(data);
onreadystatechange = function ()
{
if (objXMLHttp.readyState == 4 && (objXMLHttp.status == 200 || objXMLHttp.status == 304))
{
callback(objXMLHttp);
}
}
}
catch(e)
{
alert(e);
}
}
}
};
示例:
<script type="text/javascript" src="xmlhttp.js"></script>
<script type="text/javascript">
function test(obj)
{
alert(obj.statusText);
}
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
alert('Pool length:' + XMLHttp._objPool.length);
</script>
<script type="text/javascript">
function test(obj)
{
alert(obj.statusText);
}
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
XMLHttp.sendReq('GET', 'http://www.ugia.cn/wp-data/test.htm', '', test);
alert('Pool length:' + XMLHttp._objPool.length);
</script>
s5s5 said,
December 22, 2005 @ 7:12 pm
大哥哥,你的日志时间超前了吧,已经在BXNA显示了一天了。。。
legend said,
December 22, 2005 @ 7:19 pm
我发现这个问题了,后台的时间选项改了改,没管用,帖子时间我也改了。郁闷
再改,改到21号
danielking said,
December 23, 2005 @ 9:12 am
怎么在IE下使用有问题呢?
提示64行第5个字符,缺少标识符字符串或数字
然后提示XMLHttp未定义
在Firefox使用正常
legend said,
December 23, 2005 @ 9:39 am
源代码里有中文的注释,是ansi的,如果你的网站或ie下选择编码为UTF-8,可能会导致错误,你把你ie的编码改成gb2312看看,或者去掉文件里的中文注释
nickfan said,
December 26, 2005 @ 10:48 am
服务器是不是php5的?可能需要设置一下php.ini。
andot said,
December 27, 2005 @ 5:28 pm
测试自动转向
legend said,
December 27, 2005 @ 5:31 pm
测试自动转向
andot said,
December 27, 2005 @ 8:12 pm
测试自动跳转
野马 said,
December 29, 2005 @ 3:57 pm
思路非常好,小弟在下提几个建议:
1、“对象池”的管理功能需要加强,比如池的容量。
2、“对象池”的内存回收需要加强,比如使用完毕的对象释放内存。
3、请求最好序列一下,以控制由于服务器延时或网络延时导致的先请求的后得到结果。
4、为了代码复用和健壮,回调函数应该验证是否有效,也就是回调函数应该可以为空(可以缺省)。
legend said,
December 31, 2005 @ 9:57 am
谢谢“野马”的建议,有空再完善一下。IE默认有两个线程的限制,所以池的容量不需要很大。
hzy said,
December 31, 2005 @ 1:11 pm
看 了legend的代码,才知道啥叫高手。很想请教legend,这些mozilla和ie各自特性和差别的资料从何处得来?因为我花了2个多钟头才查到 push和pop是ie5.5以后才有的;javascript中对象的构造O={:,:,:…}是javascript1.2里面就有的(因此这样写而 不是用new object()或者function objectname(){}应该具有更好的兼容性);以及那个很有意思的addeventlistener是mozilla的DOM里面特有的。查到这 些东东,我的文件夹里面已经堆满了dom,javascript的相关规范了,而且还是E文的,这多少让我有些灰心。还有几个问题,那个load是不是 mozilla里面xmlhttprequest的事件?load对应readystate=4,那么还有没有0,1,2,3对应的其他事件?因为我在 www.xmlhttp.cn上面没有找到这个信息,xmlhttprequest我在mozilla的DOM文档里面也没有找到。另外,那个“IE默认 有两个线程的限制,所以池的容量不需要很大”这句不太明白。
感谢你的这些文章和代码,如果能够解答我的这些疑问的话,就更感谢了,呵呵。
敬佩!
hzy said,
December 31, 2005 @ 2:09 pm
PS, “Content-Type=application/x-www-form-urlencoded; charset=UTF-8″是给服务器看的,xmlhttprequest并不会因此去编码data中的数据,因此data被send之前,需要手工编 码。我在IE下面用post试了一下,自己构造了一个form表单在http请求中的形式name1=value1&name2=value2, 如果不事先编码的话,value中如果有“?”或者汉字之类的字符,就会导致出错。对于get也有类似的问题。IE下面有encodeURI等几个函数用 来对字符串编码,可以解决这个问题,但这需要浏览器版本IE5.5以上(尽管encodeURI系列是ECMAScript标准)。我找到了一个可以进行 url编码的脚本,但仅仅是那个unicode和gb2312的对应数组,就一百多K。
想请教legend,是否有比较好的通用的解决方法?谢谢。
legend said,
December 31, 2005 @ 2:52 pm
回 答hzy:一些浏览器方面的差别是我在以前做东西的时候遇到的,所以后面写代码的时候可以去避免这些问题。addeventlistener这个是参考 webfx上的一些代码。至于mozilla中xmlhttprequest里的事件,新版本中已经不存在这样的问题了,老版本未测试。
IE默认同时向一个站点发起的请求是两个,这个问题是我在做上传程序的时候遇到的,因为我同时向服务器发送三个请求,但最后一个一直得不到回应,只有等到前面两个结束后才能返回结果来。
至于urlencode这个在js里面用encodeURIComponent这个函数,各种浏览器都可以的。“Content-Type= application/x-www-form-urlencoded; charset=UTF-8"这句话是告诉服务器,我发送的数据是urlencode过的。
hzy said,
December 31, 2005 @ 3:32 pm
encodeURIComponent 在IE5.0里面没有,文档上这么说的,我没试过,所以结论下得比较武断。谢谢你耐心细致的解答。另外再跟你套个近乎,俺也是山东的。山东济南人。呵呵。 现在在青岛读研,也是26岁。呵呵。刚才好好看了你的blog一下发现了很多好东东,那个bindow,里面的图表功能很有意思,不过估计只能在IE下面 运行(vml)?
今天很高兴,知道我有一个这么厉害的老乡。赞一个!
hzy said,
January 12, 2006 @ 12:04 pm
你好,本来想直接copy你的生成xmlhttp对象的代码,发现有点问题。
在IE下面也可以手工给window添加名为XmlHttpRequest的属性,如果用户代码中恰好有这么一个自定义的属性,这段代码就不能返回正确的对象了。也许就正如你所说,代码还有可以完善的地方。
PS,看你的代码有种很high的感觉,自由而不放浪,英气逼人,呵呵。
FindRain said,
January 16, 2006 @ 10:29 am
有一个问题,是否可以给每个请求指定超时时间,加上超时处理?
mincyw said,
January 17, 2006 @ 7:12 pm
非常感谢,你的东东帮了我很大的忙!
maltose said,
January 19, 2006 @ 8:29 pm
我想请教一个关于http://www.yule21.com/info/990.htm
这篇文章说得这个问题,他是用vbscript解决的
我想请问一下用javascript就一定没有办法解决了吗?
盼望答案
longbill said,
April 23, 2006 @ 7:50 pm
为什么blog文章的标题不能复制呢?奇怪
Huangl said,
April 29, 2006 @ 12:46 am
为什么每次创建一个xmlHttprequest对象的代价需要考虑呢?即使浪费,不也是浪费clint端的那一点“小小”的性能么?对整个系统的性能会造成影响么?作者是否做过性能上的测试,或者有一些经验数据可以公布一下么?
legend said,
May 10, 2006 @ 1:30 am
没有测试过也没有经验数据,只是认为ie每次创建activex对象应该是有些花销的。我这篇文章的主要目的并不是为了节能,而是为了并发。
Huangl said,
May 10, 2006 @ 1:59 am
??不创建一个池,就不能并发了么?
还有,xmlhttprequest对象的生命周期有多长?每次从池中取出一个用就可以么?
不好意思,新手,不明白的太多,呵呵。
legend said,
May 10, 2006 @ 2:06 am
不创建也可以并发,但每次得创建一个新的xmlhttprequest对象。我创建池的目的,就是为了能够重复利用已经创建的对象。
说到并发,ie默认是每个站点同时只允许两个线程的。
这篇文章现在看来,还是有很多不妥的地方。
nada said,
May 12, 2006 @ 7:34 pm
真高手,向你学习,顶一个。
Zhang-Zi said,
May 17, 2006 @ 3:12 am
这东西不错。那个IE的Ajax缓存情况以前我遇到过,弄了我好久,到后来才发现IE的ajax会缓存。要是早看到你这篇文章就好了
An said,
June 6, 2006 @ 7:22 am
一定要说声感谢!
diaodiao said,
November 5, 2006 @ 5:43 pm
用你的代码在本地运行的时候就出现了状态代码是4 的时候回调函数没反应的情况,这个是怎么回事?
diaodiao said,
November 5, 2006 @ 8:08 pm
o~~ 试验成功了,之前不知道是哪里搞错了,多谢多谢,救急了
海南马自达 said,
December 13, 2006 @ 3:05 pm
非常感谢
Alan said,
December 31, 2006 @ 11:31 am
思 路很好,不过有个问题,IE 解释脚本的时候是顺序执行的,这就是说,他会沿你设计的脚本一步一步走下去! 而 xmlhttpRequst 则是个异步过程,这样,当下一步脚本调用前,xmlhttpResquest 总是还没有send,所以这个线程池最后也没起到作用,调用1次sendReq就多创建一个xmlHttpRequest,和没用池子几乎没什么差别
legend said,
December 31, 2006 @ 1:02 pm
調用xmlhttprequest就發送了,然後下一次調用的時候,如果前面的都在使用中,會自動創建一個新xmlhttp對象。如果有空閒的對象才會直接使用它。
這個池子並不是爲了效率,而是如果是單一的xmlhttprequest對象,重復調用的話,如果前一個正在發送,後面的就會覆蓋這個對象,那我們的請求就丟失了。
streetsmart said,
January 12, 2007 @ 3:03 am
谢谢您,您给了很大帮助。
kevin said,
April 21, 2007 @ 6:55 pm
使用中发现了一个问题。当我读取A完毕,并正确显示之后,点B,读取B的内容的时候,B可以正确显示,而A又会重新进入读取状态,而且始终无法完成,即staut始终=0。这是什么呢?怎么解决?
kevin said,
April 21, 2007 @ 6:59 pm
我代码做过改动。改动如下:(注:showid是传入的值,每次都不一样,就是显示的地方。)
onreadystatechange = function ()
{
if (objXMLHttp.readyState “;
}
if (objXMLHttp.readyState == 4 && (objXMLHttp.status == 200 || objXMLHttp.status == 304))
{
var response = objXMLHttp.responseText;
document.getElementById(showid).innerhtml=response;
legend said,
April 21, 2007 @ 8:28 pm
要不你就用这个吧: http://www.ugia.cn/?p=122
huxishengming said,
May 5, 2007 @ 3:58 pm
objXMLHttp.readyState = 0;
在ff中这样为对象添加一个属性可以吗?不是应该用addAttribute吗?
zeroone said,
May 12, 2007 @ 12:08 pm
加入随机数怎么还是没有办法解决IE的缓存
我用
xmlHttp.abort();
解决了
毛毛虫 said,
May 22, 2007 @ 9:08 am
???10)uuu(0)iii(0)ooo(0)000(0)0i(0)0889(0)45677(0)0gk(0)hh(0)u(0)p(0);(0)
ie下 请求页面得到的数据会出现如上的乱码 导致页面布局都乱了 请问这是怎么回事啊 xmlhttp.js里的中文注释我已经去掉了
ff下一切正常
legend said,
May 22, 2007 @ 9:19 am
你页面 编码是什么?
毛毛虫 said,
May 22, 2007 @ 9:26 am
html:
New Document
js:
setRequestHeader(’Content-Type’, ‘application/x-www-form-urlencoded; charset=gb2312′);
send(data);
onreadystatechange = function ()
{
if (objXMLHttp.readyState == 4 && (objXMLHttp.status == 200 || objXMLHttp.status == 304))
{
//callback(objXMLHttp);
alert(objXMLHttp.responseText);
}
}
server端:asp
毛毛虫 said,
May 22, 2007 @ 9:27 am
server端:asp
毛毛虫 said,
May 22, 2007 @ 9:28 am
啊 不让发代码啊
server端 asp:
response.charset = “gb2312″
…
毛毛虫 said,
May 22, 2007 @ 9:30 am
客户端只是发送了一个请求页面 还没参数
var u2 = “txlload/load_team_list.asp”;
XMLHttp.sendReq(’GET’,u2,'’,callback); //打开页面时 加载分组
毛毛虫 said,
May 22, 2007 @ 9:48 am
问题找到了 是创建的xmlhttprequest对象问题
我改成这样了:
if(window.XMLHttpRequest){
objXMLHttp = new XMLHttpRequest();
if (objXMLHttp.overrideMimeType) {
objXMLHttp.overrideMimeType(”text/xml”);
}
}
else if (window.ActiveXObject){
try {
objXMLHttp = new ActiveXObject(”Msxml2.XMLHTTP”);
} catch (e) {
try {
objXMLHttp = new ActiveXObject(”Microsoft.XMLHTTP”);
} catch (e) {}
}
}
if (!objXMLHttp) {
window.alert(”can’t create XMLHttpRequest object.”);
return null;
}
ie下也正常了 不过不知道是什么原因造成的
Jeffy said,
August 31, 2007 @ 10:40 am
我比较支持用json,因为rpc有性能问题
作者能告诉一下我QQ和MSN吗?
UGiA PHP UPLOADER 怎么实现远程上传?我意思是说我网站在A服务器,在A服务器页面上操作时,怎么把文件上传到B服务器上!
Jeffy said,
August 31, 2007 @ 10:42 am
上面发错地方了,应该发在 phprpc 那个里面
hongyu said,
August 31, 2007 @ 10:52 am
写得真好,正想研究下并发的xhr呢,学习了,文档注释也很全,难得啊!
胡杨 said,
October 24, 2007 @ 8:34 pm
能还能逐行的解释一下。
刚刚接触xmlhttprequest这方面的知识。
切盼
我的email:micenote@yahoo.com.cn
irene said,
December 12, 2007 @ 2:17 pm
正好遇到这种问题,需要解决。这个方法很好用!
破曉之陽 said,
December 28, 2007 @ 12:33 am
其實在IE7.0 里面。
if (window.XMLHttpRequest)
{
var objXMLHttp = new XMLHttpRequest();
}
如果把 var objXMLHttp = new XMLHttpRequest();
//var objXMLHttp=new ActiveXObject(”Microsoft.XMLhttp”); 也是可行的。就是說。在IE7.0 里面。你先創建那個他就用那個。
還有一個問題。就是當我們創建的是var objXMLHttp=new ActiveXObject(”Microsoft.XMLhttp”); 這個時候。responseText 這個東西可以用responsetext 就是說大小寫都可以的。
但是如果是var objXMLHttp = new XMLHttpRequest(); 就必須是responseText。。。。 今天我就是給這個搞了好長時間。郁悶死了。