【Javascript】解決Ajax輪詢造成的線程阻塞問題(過渡方案)


一、背景

  開發Web平台時,經常會需要定時向服務器輪詢獲取數據狀態,並且通常不僅只開一個輪詢,而是根據業務需要會產生數個輪詢。這種情況下,性能低下的Ajax長輪詢已經不能滿足需求,頻繁的訪問還會造成線程阻塞。最優的解決方案當然是用Websocket,采用服務器推送的方式來減少頻繁開關連接造成的開銷。但是Websocket對於我來說還只是個新事物,在未完成論證的情況下不能直接開發完就上,因此只好采用過渡方案,使用隊列的方式,暫時優化多AJax長輪詢的情況下造成的線程阻塞問題。

  我所用的Web平台框架是國產開源的DWZ框架。該框架不使用經典的iframe模式,所有的視圖、數據訪問都是通過Ajax獲取后在前台進行加載渲染,頁面遷移跳轉極少,因此本質上來說基於DWZ框架的網頁都是Single Page頁面。在這種情況下,除了長輪詢外,還會根據用戶的操作產生其它Ajax鏈接。這就要求在優化的同時,還要保證用戶操作的優先度。畢竟長輪詢只是后台默認執行的操作,對用戶的體驗影響不大;但用戶的操作因為長輪詢造成延遲的話,用戶體驗就十分糟糕。

  此外,我還發現處理這些Ajax輪詢所用的Controller是MVC默認的,然而這些Controller不支持異步處理請求操作,在多個請求訪問時,新請求必須等待舊請求完成后才能繼續下去。

  綜上所述,優化Ajax輪詢造成的線程阻塞問題的過渡方案中,有以下兩點要求:

    1.使用Ajax隊列的方式,不推倒現有的技術方案,在原有的基礎上快速修改。

    2.在Ajax隊列優化過程中,必須保證用戶操作的優先度,保證用戶操作的及時響應。

    3.替換原有只支持同步Action的Controller,使用可支持異常Action的Controller。

二、前台代碼解析

     總體思路是:

  1.重寫jquery既有的ajax方法,將所有調用該方法的ajax全部注冊到自定義的ajax程序池中。

  2.自定義ajax程序池分全局和非全局兩類,長輪詢發起的ajax為非全局,用戶發起的ajax為全局。

  3.排隊執行兩個程序池中的請求,一個請求完成后才繼續執行下一個,而非異步將所有ajax同時發起請求。

  4.全局ajax的優先度高,如果當前正在執行非全局ajax且有未發起的全局ajax,則停止正在執行的非全局ajax,優先發送全局ajax。

  5.非全局ajax只有在全局ajax全部完畢的情況下才會發送請求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 所有ajax請求都注冊到DNE.LoadingAjax的ajax程序池中,排隊發起請求,ajax結束時刪除.
DNE.LoadingAjax = {
     jqAjax: $.ajax,
     requests: {},  // ajax對象集合
     globalAjaxPool: [],  // 全局ajax程序池
     unglobalAjaxPool: [],  // 非全局ajax程序池
     interval:  null // ajax循環定時器
     runningType:  null // 正在運行的Ajax類型  1:全局  2:非全局
     runningId:  null , // 正在運行的AjaxId
     // 注冊Ajax到程序池中
     PushAjaxPool:  function  (request, options) {
         var  urlComplete = request.complete;
         var  requests =  this .requests;
         var  id = (request.tabId) ? request.tabId : request.url;
 
         // 請求結束時,刪除ajax對象
         request.complete =  this .deleteAjax(urlComplete, id);
 
         // 將請求放到ajax程序池中
         var  requestObj = {
             id: id,
             request: request,
             options: options
         };
 
         // 如果是獲取json數據的請求,則放入程序池中,如果是獲取Js或圖片等資源的請求,則直接執行
         if  (requestObj.request.dataType ==  "json" ) {
             if  (request.global) {
                 // 如果是全局ajax
                 this .globalAjaxPool.push(requestObj);
             else  {
                 // 如果不是全局ajax
                 this .unglobalAjaxPool.push(requestObj);
             }
         else  {
             var  loadingAjax = DNE.LoadingAjax;
             loadingAjax.runAjax(requestObj);
         }
 
 
         if  (! this .interval) {
             this .interval = window.setInterval( function  () {
 
                 var  loadingAjax = DNE.LoadingAjax;
 
                 // 如果當前有全局Ajax未運行,則停止正在運行的非全局Ajax
                 if  (loadingAjax.runningType != 1 && loadingAjax.globalAjaxPool.length > 0) {
                     if  (loadingAjax.runningType == 2 && loadingAjax.runningId) {
                         loadingAjax.ajaxAbort(id);
                     }
 
                     // 運行最開頭的全局Ajax
                     var  reqObj = loadingAjax.globalAjaxPool.shift();
                     loadingAjax.runAjax(reqObj);
 
                 else  {
                     // 如果當前沒有正在執行的Ajax,並且非全局Ajax程序池中有對象
                     if  (loadingAjax.runningType ==  null  && loadingAjax.unglobalAjaxPool.length > 0) {
 
                         // 運行最開頭的非全局Ajax
                         var  reqObj = loadingAjax.unglobalAjaxPool.shift();
                         loadingAjax.runAjax(reqObj);
                     }
                 }
             }, 100);
         }
     },
     // 刪除Ajax
     deleteAjax:  function  (urlComplete, id) {
         if  (urlComplete &&  typeof  (urlComplete) ==  "function" ) {
             urlComplete();
         }
 
         var  loadingAjax = DNE.LoadingAjax;
         if  (loadingAjax.requests[id]) {
             delete  loadingAjax.requests[id];
         }
 
         // 如果程序池中已無請求,則清空ajax循環定時器
         if  (loadingAjax.globalAjaxPool.length <= 0 && loadingAjax.unglobalAjaxPool.length <= 0) {
             loadingAjax.interval =  null ;
         }
 
         // 如果當前請求結束,則重置正在運行的Ajax類型及AjaxId
         loadingAjax.runningType =  null ;
         loadingAjax.runningId =  null ;
     },
     // 執行Ajax
     runAjax:  function  (reqObj) {
         var  jqXHR =  this .jqAjax(reqObj.request, reqObj.options);
         this .requests[reqObj.id] = jqXHR;
     },
     // 停止Ajax
     ajaxAbort:  function  (id) {
         var  jqXHR =  this .requests[id];
         if  (jqXHR) {
             jqXHR.abort();
             delete  this .requests[id];
         }
     }
};
$( function  () {
     $.extend({
         ajax:  function  (url, options) {
             // 所有ajax都注冊到ajax程序池中
             DNE.LoadingAjax.PushAjaxPool(url, options);
         }
     });
});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM