在过去的几年中,web开发已经发生了很大的变化。现如今,我们期望的是能够通过web快速、动态地访问应用。在这一新的文章系列中,我们学习如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用,以此来实现更好的用户体验。客户端的例子使用的是JQuery JavaScript库,在这首篇文章中,我们探索不同的反向Ajax技术,使用可下载的例子来学习使用了流(streaming)方法和长轮询(long polling)方法的Comet。
前言
@H_502_8@ web开发在过去的几年中有了很大的进展,我们已经远超了把静态网页链接在一起的做法,这种做法会引起浏览器的刷新,并且要等待页面的加载。现在需要的是能够通过web来访问的完全动态的应用,这些应用通常需要尽可能的快,提供近乎实时的组件。在这一新的由五部分组成的文章系列中,我们学习如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用。 @H_502_8@ 在这第一篇文章中,我们要了解反向Ajax、轮询(polling)、流(streaming)、Comet和长轮询(long polling),学习如何实现不同的反向Ajax通信技术,并探讨每种方法的优点和缺点。你可以下载本文中例子的相应源代码。 @H_502_8@ Ajax、反向Ajax和WebSocket @H_502_8@ 异步的JavaScript和XML(Asynchronous JavaScript and XML,Ajax),一种可通过JavaScript来访问的浏览器功能特性,其允许脚本向幕后的网站发送一个HTTP请求而又无需重新加载页面。Ajax的出现已经超过了十年,尽管其名字中包含了XML,但你几乎可以在Ajax请求中传送任何的东西,最常用的数据是JSON,其与JavaScript语法很接近,且消耗更少带宽。清单1给出了这样的一个例子,Ajax请求通过某个地方的邮政编码来检索该地的名称。 @H_502_8@ 清单1. Ajax请求举例
var
url
=
'
http://www.geonames.org/postalCodeLookupJSON?postalcode=
'
+ $( ' #postalCode ' ).val() + ' &country= '
+ $( ' #country ' ).val() + ' &callback=? ' ;
$.getJSON(url, function (data) {
$( ' #placeName ' ).val(data.postalcodes[ 0 ].placeName);
});
+ $( ' #postalCode ' ).val() + ' &country= '
+ $( ' #country ' ).val() + ' &callback=? ' ;
$.getJSON(url, function (data) {
$( ' #placeName ' ).val(data.postalcodes[ 0 ].placeName);
});
setInterval(
function
() {
$.getJSON( ' events ' , function (events) {
console.log(events);
});
}, 2000 );
$.getJSON( ' events ' , function (events) {
console.log(events);
});
}, 2000 );
[
client
]
checking for events...
[ client ] no event
[ client ] checking for events...
[ client ] 2 events
[ event ] At Sun Jun 05 15 : 17 : 14 EDT 2011
[ event ] At Sun Jun 05 15 : 17 : 14 EDT 2011
[ client ] checking for events...
[ client ] 1 events
[ event ] At Sun Jun 05 15 : 17 : 16 EDT 2011
[ client ] no event
[ client ] checking for events...
[ client ] 2 events
[ event ] At Sun Jun 05 15 : 17 : 14 EDT 2011
[ event ] At Sun Jun 05 15 : 17 : 14 EDT 2011
[ client ] checking for events...
[ client ] 1 events
[ event ] At Sun Jun 05 15 : 17 : 16 EDT 2011
$(
'
#submit
'
).click(
function
() {
$.post( ' ajax ' , function (data) {
var valid = data.formValid;
// 处理验证结果
// 然后处理响应的其他部分(事件)
processEvents(data.events);
});
});
$.post( ' ajax ' , function (data) {
var valid = data.formValid;
// 处理验证结果
// 然后处理响应的其他部分(事件)
processEvents(data.events);
});
});
[
client
]
checking for events...
[ server ] form valid ? true
[ client ] 4 events
[ event ] At Sun Jun 05 16 : 08 : 32 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 34 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 34 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 37 EDT 2011
[ server ] form valid ? true
[ client ] 4 events
[ event ] At Sun Jun 05 16 : 08 : 32 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 34 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 34 EDT 2011
[ event ] At Sun Jun 05 16 : 08 : 37 EDT 2011
var
xhr
=
$.ajaxSettings.xhr();
xhr.multipart = true ;
xhr.open( ' GET ' , ' ajax ' , true );
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 ) {
processEvents($.parseJSON(xhr.responseText));
}
};
xhr.send( null );
xhr.multipart = true ;
xhr.open( ' GET ' , ' ajax ' , true );
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 ) {
processEvents($.parseJSON(xhr.responseText));
}
};
xhr.send( null );
protected
void
doGet(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
// 开始请求的挂起
AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout( 0 );
// 给客户端发回多部分的分隔符
resp.setContentType( " multipart/x-mixed-replace;boundary=\" "
+ boundary + " \" " );
resp.setHeader( " Connection " , " keep-alive " );
resp.getOutputStream().print( " -- " + boundary);
resp.flushBuffer();
// 把异步上下文放在列表中以被将来只用
asyncContexts.offer(asyncContext);
}
throws ServletException,IOException {
// 开始请求的挂起
AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout( 0 );
// 给客户端发回多部分的分隔符
resp.setContentType( " multipart/x-mixed-replace;boundary=\" "
+ boundary + " \" " );
resp.setHeader( " Connection " , " keep-alive " );
resp.getOutputStream().print( " -- " + boundary);
resp.flushBuffer();
// 把异步上下文放在列表中以被将来只用
asyncContexts.offer(asyncContext);
}
for
(AsyncContext asyncContext : asyncContexts) {
HttpServletResponse peer = (HttpServletResponse)
asyncContext.getResponse();
peer.getOutputStream().println( " Content-Type: application/json " );
peer.getOutputStream().println();
peer.getOutputStream().println( new JSONArray()
.put( " At " + new Date()).toString());
peer.getOutputStream().println( " -- " + boundary);
peer.flushBuffer();
}
HttpServletResponse peer = (HttpServletResponse)
asyncContext.getResponse();
peer.getOutputStream().println( " Content-Type: application/json " );
peer.getOutputStream().println();
peer.getOutputStream().println( new JSONArray()
.put( " At " + new Date()).toString());
peer.getOutputStream().println( " -- " + boundary);
peer.flushBuffer();
}
function
long_polling() {
$.getJSON( ' ajax ' , function (events) {
processEvents(events);
long_polling();
});
}
long_polling();
$.getJSON( ' ajax ' , function (events) {
processEvents(events);
long_polling();
});
}
long_polling();
AsyncContext asyncContext
=
req.startAsync();
asyncContext.setTimeout( 0 );
asyncContexts.offer(asyncContext);
}
asyncContext.setTimeout( 0 );
asyncContexts.offer(asyncContext);
}