出處:http://www.cnblogs.com/GerryOfZhong/p/6135288.html
作者:半個詩人
上一篇(框架基礎:ajax設計方案(一)---集成核心請求)文章介紹了ajax技術核心方法,和跨域的問題(只要后臺支持跨域默認post就可以),這篇文章講解一下使用ajax實現(xiàn)的輪詢技術,至于iframe,SSE服務器單向推送,以及webSocket雙工通道暫時不涉及。
一些概念:
- 短輪詢:瀏覽器通過循環(huán)或者setTimeout方法,每隔一段時間往后臺發(fā)送一次請求,無線循環(huán)
- 長輪詢:不停的向后臺請求數(shù)據(jù),但是后臺如果檢測不到數(shù)據(jù)變動,就會將這個請求掛掉。如果檢測到數(shù)據(jù)變動,就會響應這個請求變動數(shù)據(jù)
區(qū)別概念:
- 長連接:在進行http數(shù)據(jù)傳輸?shù)臅r候,在數(shù)據(jù)傳輸層一直開著一個TCP通道,所有請求資源文件都是通過復用這個通道去請求數(shù)據(jù),有超時時間
- 短連接:如果http進行的短連接,即每次瀏覽器發(fā)送請求,都會創(chuàng)建TCP通道,然后傳輸完成了再進行銷毀,重復操作,消耗很大
主要區(qū)別:
- http的長短輪詢,通過代碼層,向后臺請求數(shù)據(jù)。
- http的長短連接,實際上就是TCP協(xié)議傳輸層是否復用一個TCP協(xié)議。
主要業(yè)務方面:及時性比較高的應用(web端聊天系統(tǒng)),或者需要后臺等待響應的應用(比如付款,等待完成響應)。
關鍵代碼:
/*
* 長輪詢的實現(xiàn)
* a. 業(yè)務上只需要得到服務器一次響應的輪詢
* b. 業(yè)務上需要無限次得到服務器響應的輪詢
*
* param: url 請求接口地址
* data 請求參數(shù)
* successEvent 成功事件處理
* isAll 是否一直請求(例如,等待付款完成業(yè)務,只需要請求一次)
* timeout ajax超時時間
* timeFrequency 每隔多少時間發(fā)送一次請求
* error 錯誤事件
* timeout 超時處理
* */
longPolling:function(url,data,successEvent,isAll,timeout,timeFrequency,errorEvent,timeoutEvent){
var ajaxParam ={
time:timeout,
type:"post",
url:url,
data:data,
async:false,
success:function(date){
successEvent(data);
var timer = setTimeout(
function(){
tempObj.longPolling(url,data,successEvent,isAll,error,timeoutEvent);
},timeFrequency);
//業(yè)務需求判斷,是否只需要得到一次結(jié)果
if (!isAll) clearTimeout(timer);
},
//如果走了error說明該接口有問題,沒必要繼續(xù)下去了
error:errorEvent,
timeout:function(){
timeoutEvent();
setTimeout(function(){
tempObj.longPolling(url,data,successEvent,isAll,error,timeoutEvent)
},timeFrequency);
}
};
ajax.common(ajaxParam);
}
考慮到業(yè)務需求,集成了一次isAll參數(shù)有2個意義
聊天系統(tǒng)會要一直需求輪詢,不間斷的向后臺使用數(shù)據(jù),所以isAll = true
等待付款業(yè)務只需要得到后臺一次響應是否支付成功,所以isAll = false
稍微提及一下遇到的一些問題:
問題:
success:function(date){
successEvent(data); //此處使用遞歸,不停遞歸自己
tempObj.longPolling(url,data,successEvent,isAll,error,timeoutEvent);
},
瀏覽器報錯:
Uncaught RangeError: Maximum call stack size exceeded.
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
at Object.success (ajax-1.2.js:266)
at XMLHttpRequest.xhr.onload (ajax-1.2.js:160)
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
at Object.success (ajax-1.2.js:266)
at XMLHttpRequest.xhr.onload (ajax-1.2.js:160)
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
common @ ajax-1.2.js:202
longPolling @ ajax-1.2.js:280
success @ ajax-1.2.js:266
xhr.onload @ ajax-1.2.js:160
(anonymous) @ index.html:42
(anonymous) @ index.html:43
ajax-1.2.js:202 Uncaught RangeError: Maximum call stack size exceeded.
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
at Object.success (ajax-1.2.js:266)
at XMLHttpRequest.xhr.onload (ajax-1.2.js:160)
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
at Object.success (ajax-1.2.js:266)
at XMLHttpRequest.xhr.onload (ajax-1.2.js:160)
at Object.common (ajax-1.2.js:202)
at Object.longPolling (ajax-1.2.js:280)
英文解釋:
超出最大調(diào)用堆棧大小。
問題原因:
遞歸調(diào)用過多導致的棧溢出問題說明
問題解釋:
函數(shù)調(diào)用的參數(shù)是通過??臻g來傳遞的,在調(diào)用過程中會占用線程的棧資源。而遞歸調(diào)用,只有走到最后的結(jié)束點后函數(shù)才能依次退出,而未到達最后的結(jié)束點之前,占用的棧空間一直沒有釋放,如果遞歸調(diào)用次數(shù)過多,就可能導致占用的棧資源超過線程的最大值,從而導致棧溢出,導致程序的異常退出。js可以調(diào)用自身,這里不停的調(diào)用longPolling方法,在方法里面不停的調(diào)用自己,導致GC(垃圾回收)一直不釋放,越來越大,導致資源超過最大上限,直接崩潰。然后級聯(lián)一層一層的拋出崩潰信息
解決方案:
使用settimeout解決該問題
方案解釋:
因為Javascript是單線程的,有個排隊的處理隊列,所以settimeout相當于有一個計時器,不停的向這個隊列每隔一段時間塞進一個處理事件。因為這樣,相當于longPolling方法每次都走完了,GC就將該方法的資源釋放了,然后再執(zhí)行,再釋放。
代碼已集成github:GerryIsWarrior/ajax,點顆星星是我最大的鼓勵,下一步研究ajax的上傳文件技術(H5的)
PS:對于輪詢這個技術,雖然平時用的少,但是在一些特殊的業(yè)務場景能發(fā)揮很大的作用。在瀏覽器,沒有完完全全支持H5的境況下,這個還是要考慮的。畢竟H5的那些webSocket還是需要H5兼容的。而且,研究這一塊,對原聲js,和計算機的一些底層技術還是很有幫助的,像堆棧溢出,不僅僅是前端,后端也會遇到。這樣的話,自己底層更夯實,對于以后上層的發(fā)展也會有更好的增長。
本文完!
在學習過程如果有任何疑問,請來極樂網(wǎng)(http://www.dreawer.com提問,或者掃描下方二維碼,關注極樂官方微信,在平臺下方留言~
