上篇文章有些東西言猶未盡,這里補遺,使之理解更為全面些。
還多虧了jQuery,提供了JSONP請求的方法:
$.getJSON('http://192.168.0.99/servlet3/getjsonpnew?callback=?')
指定需要回調的參數名
callback,但其值使用一問號替代,作為占位符,jQuery會自動貼心的生成一個隨機的調用函數名,服務器端的生成結果:
jQuery150832738454006945_1297761629067('2011-02-15 17:20:33 921');
我們直接在success函數中直接獲取傳入的值,無須擔心那個看起來怪怪的回調函數名。JSONP的原理,在頁面頭部區動態產生一個JS文件引用(有人稱為動態標簽引用)。
<script src="http://192.168.0.99:8080/servlet3/getNextTime2?callback=jQuery150832738454006945_1297761629067&_=1297761631777"/>
雖然這一切看起來很好,很貼心,但也存在著不少問題:
- 無法直接顯示指定回調函數名完美主義者可能無法忍受長長的、怪怪的函數名;當然了,可以定制的東西,總是讓人很舒心
- 在長連接情況下,瀏覽器會一直顯示正在加載中圖示jQuery生成的JSONP JS文件在長連接情況下,會一直加載中,可能會影響到良好的用戶體驗,也會讓前端UI體驗師比較鬧心。
很顯然jQuery無法滿足我們更高的實際要求。怎么辦,請出
jQuery-JSONP組件,基本上可以滿足我們的要求。服務器端代碼沒有什么變化,有所變化的是客戶端代碼。
<html>
<head>
<title>Comet JSONP TEST</title>
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
<meta name="author" content="yongboy@gmail.com"/>
<meta name="keywords" content="servlet3, comet, ajax"/>
<meta name="description" content=""/>
<link type="text/css" rel="stylesheet" href="css/main.css"/>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.min.js"></script>
<script type="text/javascript" src="js/jquery.jsonp-2.1.4.js"></script>
<script type="text/javascript">
String.prototype.template=function(){
var args=arguments;
return this.replace(/\{(\d )\}/g, function(m, i){
return args[i];
});
}
var html = '<div class="logDiv">'
'<div class="contentDiv">{0}</div>'
'<div class="tipDiv">last date : {1}</div>'
'<div class="clear"> </div>'
'</div>';
function showContent(json) {
$("#showDiv").prepend(html.template(json.content, json.date));
}
var jsonpUrl = "http://192.168.0.99/servlet3/getjsonpnew";
function initJsonp(){
$.jsonp({
url: jsonpUrl,
callbackParameter: 'callback',
callback : 'yongboy', //eg:http://192.168.0.99/servlet3/getjsonpnew?callback=yongboy&_1297934909500=
success: function(json) {
if(json.state == 1){
showContent(json);
}
initJsonp();
},error: function(data) {
}
});
}
$(function (){
// 為了兼容opera瀏覽器,設置為20秒延時時間(我本地測試若大于20s,可能出現連接中斷問題)
if($.browser.opera){
jsonpUrl = "?timeout=20000";
}
initJsonp();
});
</script>
</head>
<body style="margin: 0; overflow: hidden">
<div id="showDiv" class="inputStyle">loading ...</div>
</body>
</html>
嗯,jQuery-JSONP組件提供了更為靈活的jsonp參數配置:
- 指定回調函數名
- 提供錯誤處理函數,兼容opera
- 在目前大部分瀏覽器中沒有加載進度顯示
經測試:
- IE
頁面不刷新,效果很滿意。jQuery-JSONP源代碼:
if ( browser.msie ) {
script.event = STR_ONCLICK;
script.htmlFor = script.id;
script[ STR_ONREADYSTATECHANGE ] = function() {
/loaded|complete/.test( script.readyState ) && callback();
};
// All others: standard handlers
}
使用了IE瀏覽器onreadystatechange加載事件等特性。 - Firefox
script[ STR_ONERROR ] = script[ STR_ONLOAD ] = callback;
browser.opera ?
// Opera: onerror is not called, use synchronized script execution
( ( scriptAfter = $( STR_SCRIPT_TAG )[ 0 ] ).text = "jQuery('#" + script.id + "')[0]." + STR_ONERROR + "()" )
// Firefox: set script as async to avoid blocking scripts (3.6+ only)
: script[ STR_ASYNC ] = STR_ASYNC;
看來我當前瀏覽器中3.6版本的火狐支持腳本文件async屬性,非阻塞加載,再添加上onload,onerror事件支持,頁面不刷新顯示,也不難辦到了。 - Chrome
很不錯,當前作為主力的Chrome瀏覽器也支持腳本文件的async屬性,效果同3.6版本火狐。 - Opera
情況很糟糕,在Windows Opera 11.01以及Ubuntu Opera 11.00下測試,長輪詢時間若長于20秒,則會出現連接中斷情況。樣例中在Opear瀏覽器下設置長輪詢過期時間為20s。
瀏覽器會一直顯示為正在加載中的狀態。
Opera瀏覽器中腳本加載不支持onerror事件,只能使用阻塞式的腳本加載機制用以處理有可能發生的異常。
有關阻塞方式實現JSONP,延伸閱讀:
Comet (long polling) for all browsers using ScriptCommunicator - Safari
既然同為一個內核的谷歌瀏覽器也支持腳本文件的async屬性,同樣的異步加載也是水到渠成的了。