//////////////////////// 2010-02-19 update /////////////////////////////
对于实现这个轮询发出请求来更新页面UI的功能,还可以通过setInterval定时器来实现,不过普通的定时器的实现方式是在规定的时间内重复的发出请求,而不管前面的请求是否已经完成,这样的实现方式是有很多弊端的。从这点出发,我修改了通过定时器setInterval的实现方式,通过一个布尔值来判断前面的请求是否完成,没完成的话就压缩到堆栈里等待执行。下面是实现的源码:
/*
* @author Shanpeng
* @website:http://www.ilovejs.net
* @e-mail:supersha@foxmail/shanpeng@taobao.com/supershafeng@gmail.com
* @QQ:770104121
* @my word:Here,believe what browsers display,and believe yourself
*/
////////////////////// Javascript's code below ///////////////////////
(function(global){
var XHR = function(){
var xhr = false;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject("Msxml2.XMLHTTP")) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
var query = [];
var xhr = XHR();
var ready = true; //用于判断某个请求响应是否成功
var queryAjax = function(url, callback){
query[query.length] = [url, callback];
if (ready) {
var cur = query.shift();
xhr.open("GET", cur[0], true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) {
cur[1](xhr);
ready = true;
}
}
xhr.send(null);
}
ready = false;
}
global.queryAjax = queryAjax;
})(this);
//Usage:
onload = function(){
var div = document.getElementById("test");
var i = 0;
setInterval(function(){
queryAjax("test.txt", function(obj){
div.innerHTML = obj.responseText + (++i);
});
}, 1000);
}
///////////////////////////////////////////////////////////////////
前几天接触到关于长轮询调用Ajax的技巧,原理很简单:就是在前一次Ajax调用并且响应成功之后再调用XHR对象的open和send方法,继续发送请求。代码示例如下:
var sendRequest = function(url, callback){
var xhr = XHR();
if (typeof xhr === "string") throw xhr;
xhr.open("GET", url, true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200) {
//调用回调函数处理响应
callback(xhr);
//重新打开连接
xhr.open("GET", url, true);
xhr.send(null);
}
}
xhr.send(null);
}
长轮询的关键点就在onreadystatechange事件内,当响应成功之后再打开连接,发送请求。这个比起使用定时器的方式又优化很多了。定时器的代码示例如下:
var sendRequest=function(url,callback){
var xhr=XHR();
if(typeof xhr === "string") throw xhr;
xhr.open("GET",url,true);
xhr.onreadystatechange=function(){
if(xhr.readyState === 4 && xhr.status === 200){
callback(xhr);
}
}
xhr.send(null):
}
//设置定时器
setInterval(sendRequest,1000);
定时器技巧可能会引发一个致命的问题,就是在时间间隔内如果响应还没有完成,下一个请求就又发出了,这将会导致过多的无用请求,同时也会拖慢系统的运行。
但是这篇文章并不是来说明定时器技巧和长轮询的优异,而是为了说明我在测试长轮询的过程中发现的一个问题。就拿上面第一段展示长轮询的代码来说,如果在IE8下,将会导致崩溃;Google Chrome、Safari、Opera和Firefox下正常。《测试页面》
我通过将定时器跟长轮询结合起来,就可以避免这个问题,在各个浏览器下测试正常,代码示例如下:
var sendRequest = function(url, callback){
var xhr = XHR();
if (typeof xhr === "string")
throw xhr;
xhr.open("GET", url, true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4) {
callback(xhr);
//设置一个0时间间隔的定时器
setTimeout(function(){
xhr.open("GET", url, true);
xhr.send(null);
}, 0);
}
}
xhr.send(null);
}
在上面的代码示例中,在onreadystatechange事件中在响应成功之后设置一个0时间间隔的定时器,这就解决了IE下的问题。《测试页面》,原理可能是定时器本身需要一个生成并执行的过程,这个就给了javascript解析器一个缓冲的过程,从而避免了“死循环”使得javascript解析器错误的解析判断代码。
在IE下崩溃的主要原因一方面是“死循环”,但是主要还是回调函数比较简单,执行的时间非常的短,使得IE的JScript解析器执行的次数超过了极限。或许回调函数如果能执行比较长时间的话,IE的这个问题也就无需使用0定时器了。