• 【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全部完毕的情况下才会发送请求。

    // 所有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);
            }
        });
    });

      三、后台代码解析(待续)

  • 相关阅读:
    cookie,session,django中间件,csrf回顾
    CSRF
    django中间件
    cookie与session
    form组件简单回顾
    分页器组件与form组件
    ajax回顾
    AJAX
    python魔法方法详解
    python静态方法类方法属性方法
  • 原文地址:https://www.cnblogs.com/nonkicat/p/5105033.html
Copyright © 2020-2023  润新知