解决一次要发出多个ajax请求

jquery ajax队列管理插件

不错的ajax的jquery队列管理器

;(function($) {
 function AjaxQueue(override) {
 this.override = !!override;
 };
 AjaxQueue.prototype = {
 requests: new Array(),
 offer: function(options) {
 var _self = this;
 var xhrOptions = $.extend({}, options, {
 complete: function(jqXHR, textStatus) {
 if($.isArray(options.complete)) {
 var funcs = options.complete;
 for(var i = 0, len = funcs.length; i < len; i++)
 funcs[i].call(this, jqXHR, textStatus);
 } else {
 if(options.complete)
 options.complete.call(this, jqXHR, textStatus);
 }
 _self.poll();
 },
 beforeSend: function(jqXHR, settings) {
 if(options.beforeSend)
 var ret = options.beforeSend.call(this, jqXHR, settings);
 if(ret === false) {
 _self.poll();
 return ret;
 }
 }
 });
 if(this.override) {
 this.replace(xhrOptions);
 } else {
 this.requests.push(xhrOptions);
 if(this.requests.length == 1) {
 $.ajax(xhrOptions);
 }
 }
 },
 replace: function(xhrOptions) {
 var prevRet = this.peek();
 if(prevRet != null) {
 prevRet.abort();
 }
 this.requests.shift();
 this.requests.push($.ajax(xhrOptions));
 },
 poll: function() {
 if(this.isEmpty()) {
 return null;
 }
 var processedRequest = this.requests.shift();
 var nextRequest = this.peek();
 if(nextRequest != null) {
 $.ajax(nextRequest);
 }
 return processedRequest;
 },
 peek: function() {
 if(this.isEmpty()) {
 return null;
 }
 var nextRequest = this.requests[0];
 return nextRequest;
 },
 isEmpty: function() {
 return this.requests.length == 0;
 }
 };
 var queue = {};
 var AjaxManager = {
 createQueue: function(name, override) {
 return queue[name] = new AjaxQueue(override);
 },
 destroyQueue: function(name) {
 if(queue[name]) {
 queue[name] = null;
 delete queue[name];
 }
 },
 getQueue: function(name) {
 return ( queue[name] ? queue[name] : null);
 }
 };
 $.AM = AjaxManager;
})(jQuery);
使用示例:
var newQueue = $.AM.createQueue('queue');
$(function(){
          newQueue.offer({url:'?c=Message&m=write&a=10'});
          newQueue.offer({url:'?c=Message&m=write&a=10'});
          newQueue.offer({url:'?c=Message&m=write&a=1'});
});

第二个插件

(function($) {
  
    var ajax = $.ajax,
        pendingRequests = {},
        synced = [],
        syncedData = [],
        ajaxRunning = [];
  
  
    $.ajax = function(settings) {
        // create settings for compatibility with ajaxSetup
        settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings));
  
        var port = settings.port;
  
        switch (settings.mode) {
            case "abort":
                if (pendingRequests[port]) {
                    pendingRequests[port].abort();
                }
                return pendingRequests[port] = ajax.apply(this, arguments);
            case "queue":
                var _old = settings.complete;
                settings.complete = function() {
                    if (_old) {
                        _old.apply(this, arguments);
                    }
                    if (jQuery([ajax]).queue("ajax" + port).length > 0) {
                        jQuery([ajax]).dequeue("ajax" + port);
                    } else {
                        ajaxRunning[port] = false;
                    }
                };
  
                jQuery([ajax]).queue("ajax" + port, function() {
                    ajax(settings);
                });
  
                if (jQuery([ajax]).queue("ajax" + port).length == 1 && !ajaxRunning[port]) {
                    ajaxRunning[port] = true;
                    jQuery([ajax]).dequeue("ajax" + port);
                }
  
                return;
            case "sync":
                var pos = synced.length;
  
                synced[pos] = {
                    error: settings.error,
                    success: settings.success,
                    complete: settings.complete,
                    done: false
                };
  
                syncedData[pos] = {
                    error: [],
                    success: [],
                    complete: []
                };
  
                settings.error = function() { syncedData[pos].error = arguments; };
                settings.success = function() { syncedData[pos].success = arguments; };
                settings.complete = function() {
                    syncedData[pos].complete = arguments;
                    synced[pos].done = true;
  
                    if (pos == 0 || !synced[pos - 1])
                        for (var i = pos; i < synced.length && synced[i].done; i++) {
                        if (synced[i].error) synced[i].error.apply(jQuery, syncedData[i].error);
                        if (synced[i].success) synced[i].success.apply(jQuery, syncedData[i].success);
                        if (synced[i].complete) synced[i].complete.apply(jQuery, syncedData[i].complete);
  
                        synced[i] = null;
                        syncedData[i] = null;
                    }
                };
        }
        return ajax.apply(this, arguments);
    };
  
})(jQuery);



(function(){
    $("body").queue([]);
    $("#dtitle").click(function(){
        $.ajax({
            url: "test.php?t=" + new Date().getMilliseconds(),
            success: function(html){
                jQuery("ul").append(html);
            },
            //用abort而不用queue,是因为需求是需要最后一个ajax request,而之前的ajax request
            //其实并没有用,那何必要等它们执行完呢?中途就可以把它中止掉
            mode: "abort"
        });
    });
});

 第三各ajax高级程序设计上的。

/*
 * 将ajax根据优先级进行排列的方法
 * 构造一个简单的排列函数,接受一个排序的函数
 * 所有添加的ajax保存到_items中
 * 
 */
function PriorityQueue(fn){
    this._items = [];
    if(typeof fn == 'function'){
        this._compare = fn;
    }
}
 
PriorityQueue.prototype = {
    constructor:PriorityQueue,
    _compare:function(oValue1,oVlaue2){
        if(oValue1<oVlaue2){
            return -1;
        }else if(oValue1 > oVlaue2){
            return 1;
        }else{
            return 0;
        }
    },
    //排序
    prioritze:function(){
        this._items.sort(this._compare)
    },
    //移除并返回第一个ajax  
    get:function(){
        return this._items.shift();
    },
    //返回对列中指定的ajax
    item:function(iPos){
        return this._items[iPos];
    },
    //返回队列中第一个ajax
    peek:function(){
        return this._items[0];
    },
    //将一个ajax插入到队列中,并排序
    put:function(oValue){
         this._items.push(oValue);
         this.prioritze();
    },
    //返回队列的长度
    size:function(){
        return this._items.length;
    },
    //移除一个指定的ajax,成功后返回true,否则false
    remove:function(oValue){
        for(var i=0,len=this._items.length;i<len;i++){
            if(this._items[i] == oValue){
                this._items.splice(i,1);  
                return true;
            }
        };
        return false;
    }
}
 
/*
 * 
 * 
 */
var RequestManager = (function(){
    var oManager =  {
        //队列是最长等待时间
        AGE_LIMIT:60000,
        //默认优先级10
        DEFAULT_PRIORTY:10,
        //检查队列时间间隔
        INTERVAL:250,
        //保存正在执行的ajax
        _active:[],
        //队列实例
        _pending:new PriorityQueue(function(oRequest1,oRequest2){
            return oRequest1.priority - oRequest2.priority;
        }),
        //检查每个ajax的等待时间,如果超出默认的最长时间,则提高该ajax的优先级
        _agePromote:function(){
            for(var i=0;i<this._pending.size();i++){
                var oRequest = this._pending._items[i];
                oRequest.age += this.INTERVAL;
                if(oRequest.age >= this.AGE_LIMIT){
                    oRequest.age = 0;
                    oRequest.priority--;
                };
            };
            this._pending.prioritze();
        },
        //检查正在执行的ajax状态,
        _checkActiveRequests:function(){
            var oRequest = null;
            var oTransport = null;
             
            for(var i=this._active.length-1; i>=0; i--){
                oRequest = this._active[i];
                oTransport = oRequest.transport;
                if(oTransport.readyState == 4){
                    oRequest.active = false;
                    this._active.splice(i,1);
                    var fnCallback = null;
                    if(oTransport.status >= 200 && oTransport.status < 300){
                        if(typeof oRequest.onsuccess == 'function'){
                            fnCallback = oRequest.onsuccess;
                        }
                    }else if(oTransport.status == 304){
                        if(typeof oRequest.onnotmodified == 'function'){
                            fnCallback = oRequest.onnotmodified;
                        }
                    }else{
                        if(typeof oRequest.onfailure == 'function'){
                            fnCallback = oRequest.onfailure;
                        }
                    }
                    if(fnCallback != null){
                        setTimeout((function(fnCallback,oRequest,oTransport){
                            return function(){
                                 fnCallback.call(oRequest.scope||window, { 
                                    status : oTransport.status, 
                                    data : oTransport.responseText, 
                                    request : oRequest
                                })
                            }
                        })(fnCallback,oRequest,oTransport),1);
                    }
                }
            }
        },
        //封装XMLHttpRequest
        _createTransprot:function(){
            if(typeof XMLHttpRequest != 'undefined'){
                return new XMLHttpRequest();
            }else if(typeof ActiveXObject != 'undefined'){
                var xhr = null;
                try{
                    xhr = new ActiveXObject('MSXML2.XmlHttp.6.0');
                    return xhr;
                }catch(e){
                    try{
                        xhr  = new ActiveXObject('MSXML2.XmlHttp.3.0');
                        return xhr;
                    }catch(e){
                        throw Error('cannot create XMLHttp object!');
                    }
                }
            }
        },
        //发送一下个请求,检查当前执行的ajax是否小于2,如果是,则激活下一个ajax
        _sendNext:function(){
            if(this._active.length <2){
                var oRequest = this._pending.get();
                if(oRequest != null){
                    this._active.push(oRequest);
                    oRequest.transport = this._createTransprot();
                    oRequest.transport.open(oRequest.type,oRequest.url,true);
                    oRequest.transport.send(oRequest.data);
                    oRequest.active = true;
                }
            }
        },
        //取消指定的ajax,如果有回调函数oncancel,则执行
        cancel:function(oRequest){
            if(!this._pending.remove(oRequest)){
                oRequest.transport.abort();
                if(this._active[0] === oRequest){
                    this._active.shift();
                }else if(this._active[1] === oRequest){
                    this._active.pop();
                };
                if(typeof oRequest.oncancel == 'function'){
                    oRequest.oncancel.call(oRequest.scope||window,{request:oRequest})
                }
            }
        },
        //添加一个ajax到队列中
        send:function(oRequest){
            if(typeof oRequest.priority != 'number'){
                oRequest.priority = this.DEFAULT_PRIORTY;
            };
            oRequest.active = false;
            oRequest.age = 0;
            this._pending.put(oRequest);
        },
         
        /*
         * 预置一些方面,方便不知道该如何设置优先的情况
         * 其实也就是给这些方法加了个默认的优先级
         */
        poll:function(oRequest){
            oRequest.priority = 3;
            this.send(oRequest);
        },
         
        prefetch:function(oRequest){
            oRequest.priority = 5;
            this.send(oRequest);
        },
         
        submit:function(oRequest){
            oRequest.priority = 0;
            this.send(oRequest);
        },
         
        submitPart:function(oRequest){
            oRequest.priority = 2;
            this.send(oRequest);
        },
    };
    //通过setInterval,不断的检查队列中ajax执行情况,
    //如果执行完,添加下一个
    //如果超过最长等待时间,则提高优先级
    //据说这里之所以不用onreadystatechange是为了避免IE下的内存问题
    //但感觉这样在页面上不停的setinterval,同样让人蛋疼啊!
    setInterval(function(){
        RequestManager._checkActiveRequests();
        RequestManager._sendNext();
        RequestManager._agePromote();
    },oManager.INTERVAL);
     
    return oManager;
})();
 
/*
用法:
    RequestManager.send({
        priority:0,
        type:'get',
        url:'data.txt',
        onsuccess:function(){},
        onfailure:function(){},
        onnotmodified:function(){}
    })
*/

 第四:

/*
 完成请求的赛跑

 异步请求几乎同时发生,但却不会同步而且次序经常颠倒。

 1.延迟决定胜利者
 在赛跑过程中,无论是服务器还是脚本都无法使某个请求更快地得到响应。请求过程中的延迟会出现在几个阶段,而其中的多个阶段都是你无法控制的。
 所有通信过程都将遵循如下相同的模式:
 (1)客户端计算机对服务器发起获取或修改信息的请求。
 (2)将请求通过一条计算机网络发送到服务器。
 (3)服务器处理请求。
 (4)服务器将对请求的响应通过另一条计算机网络发送回客户端计算机
 在这个请求/相应循环的过程中,每个阶段都存在外部因素的影响。

 2.处理异步请求
 处理请求/响应循环中的延迟问题有很多不同的方式。以下是其中几种主要的思路:
 (1)置之不理。
 (2)关掉异步行为。 在Ajax对象中设置asynchronous=false是另外一种选择,但这个选择也可以从你的方案清单中划掉。如果在XMLHttpRequest对象上设置了同步模式,那么它会按照次序处理请求,但它是通过把请求转换为一种更加激进的阻塞模式来实现这一点的。在这种情况下,你的脚本将被迫停止运行直至请求完成,期间可能会因为响应过慢而导致脚本和浏览器被挂起。

 3.在客户端队请求排队
 排队是另一种可能的方案。与其一次发送多个XMLHttpRequest请求,不如等到前一个请求获得相应后再发送下一个。

 */

/* 一个复制对象的辅助方法 */
function clone(myObj) {
    if (typeof myObj !== 'object') {
        return myObj;
    }
    if (myObj === null) {
        return myObj;
    }
    var myNewObj = {};
    for (var i in myObj) {
        myNewObj[i] = clone(myObj[i]);
    }
    return myNewObj;
}

/* 用于保存队列的数组 */
var requestQueue = [];

/**
 * 为ADS.ajaxRequest方法启用排队功能的包装对象
 * @param url
 * @param options
 * @param queue
 * @example
 *      ADS.ajaxRequestQueue('/your/script/', {
     *          completeListener: function(){
     *              alert(this.responseText);
     *          }
     *      }, 'Queue1');
 */
function ajaxRequestQueue(url, options, queue) {
    queue = queue || 'default';

    // 这个对象将把可选的侦听器包装在另一个函数中
    // 因此,可选的对象必须唯一。否则,如果该方法
    // 被调用时使用的是共享的可选对象,那么会导致
    // 陷入递归中
    options = clone(options) || {};
    if (!requestQueue[queue]) {
        requestQueue[queue] = [];
    }

    // 当前一次请求完成时,需要使用completeListener
    // 调用队列中的下一次请求。如果完成侦听器已经
    // 有定义,那么需要首先调用它

    // 取得旧侦听器
    var userCompleteListener = options.completeListener;

    // 添加新侦听器
    options.completeListener = function () {
        // 如果存在旧的侦听器则首先调用它
        if (userCompleteListener) {
            // this引用的是情求对象
            userCompleteListener.apply(this, arguments);
        }

        // 从队列中移除这个请求
        requestQueue[queue].shift();

        // 调用队列中的下一项
        if (requestQueue[queue][0]) {
            // 请求保存在req属性中,但为防止它是
            // 一个POST请求,故也需包含send选项
            var q = requestQueue[queue][0].req.send(
                    requestQueue[queue][0].send
            );
        }
    };

    // 如果发生了错误,应该通过调用相应的
    // 错误处理方法取消队列中的其他请求

    // 取得旧侦听器
    var userErrorListener = options.errorListener;

    // 添加新侦听器
    options.errorListener = function () {
        if (userErrorListener) {
            userErrorListener.apply(this, arguments);
        }

        // 由于已经调用了错误侦听器
        // 股从队列中移除这个请求
        requestQueue[queue].shift();

        // 由于出错需要取消队列中的其余请求,但首先要调用
        // 每个请求的errorListener。通过调用队列中
        // 下一项的错误侦听器就会才清楚所有排队的请求,因为在
        // 链中的调研那个是一次发生的

        // 检测队列中是否还存在请求
        if (requestQueue[queue].length) {
            // 取得下一项
            var q = requestQueue[queue].shift();

            // 中断请求
            q.req.abort();

            // 伪造请求对象,以便errorListener
            // 认为请求已经完成并相应地运行

            var fakeRequest = {};

            // 将status设置为0,将readyState设置为4
            // 就好像请求虽然完成但却失败了一样
            fakeRequest.status = 0;
            fakeRequest.readyState = 4;

            fakeRequest.responseText = null;
            fakeRequest.responseXML = null;

            // 设置错误信息,以便需要时显示
            fakeRequest.statusText = 'A request in the queue received an error';

            // 调用状态改变,如果readyState是4,而
            // status不是200,则会调用errorListener
            q.error.apply(fakeRequest);
        }
    };

    // 将这个请求添加到队列中
    requestQueue[queue].push({
        req: getRequestObject(url, options),
        send: options.send,
        error: options.errorListener
    });

    // 如果队列的长度表明只有一个
    // 项(即第一个)则调用请求
    if (requestQueue[queue].length === 1) {
        ajaxRequest(url, options);
    }
}

window.ADS.ajaxRequestQueue = ajaxRequestQueue;

//队列中的请求1
ADS.ajaxRequestQueue('/your/script/', {
    completeListener: function () {
        alert(this.responseText);
    }
}, 'Queue1');
//队列中的请求2
ADS.ajaxRequestQueue('/your/script/', {
    completeListener: function () {
        alert(this.responseText);
    }
}, 'Queue2');
//队列1中的请求1,要等到请求1完成
ADS.ajaxRequestQueue('/your/script/', {
    completeListener: function () {
        alert(this.responseText);
    }
}, 'Queue1');
// 队列1与队列2会在同一时刻以异步方式运行

/*
 4.令请求异步但禁用有冲突的功能
 禁用功能可能是避免不协调问题的最常见方法了。当执行某些异步请求时,让用户知道后台在干什么永远是很重要的。而这通常是通过在请求等待响应期间显示“载入中”等信息或者动画来完成。在等待期间,用户可能会犹豫急不可耐而在载入完成之前有执行了相同的操作,那么就可能对程序造成潜在的破坏。
 除了显示简单的“载入中”信息之外,还可以禁用程序中的某个部件,以防止用户在不耐烦的情况下重复操作。而实现这一点的唯一技巧,就是无论是响应成功,还是发生了错误都要重新启用所禁用的部件。
 */

// 例如:Web应用程序中包含如下提交按钮
// <input type="submit" id="buttonID">
// 那么,就可以禁用表单提交功能
ADS.ajaxRequest('/your/script/', {
    loadListener: function () {
        // 在载入期间禁用按钮
        ADS.$('buttonID').disabled = 'disabled';
    },
    completeListener: function () {
        // 当响应成功时启用按钮
        ADS.$('buttonID').disabled = '';
        alert(this.responseText);
    },
    errorListener: function () {
        // 当发生错误时也要启用按钮
        ADS.$('buttonID').disabled = '';
        alert('Oops, please try again:' + this.statusText);
    }
});

/*
这种方法的唯一问题,就是在某些情况下--比如拖放式界面功能中--如果使用它,结果差不多会与传统页面重载的工作流一样令人讨厌。所以不能再脚本等待响应期间禁用拖动功能
 */

// 增加资源占用

 

posted @ 2015-03-30 22:41  焚情、烈日  阅读(4204)  评论(0编辑  收藏  举报