zoukankan      html  css  js  c++  java
  • jQuery ajax —— 主函数分析

    由于jQuery ajax对Callbacks、Deferred、serialize、event等模块的依赖,建议对这些模块没有认识的朋友看一下jQuery CallbacksjQuery DeferredjQuery serializejQuery event(上)jQuery event(下)

    这篇文章主要分析的是拥有380+行的jQuery.ajax函数,该函数是jQuery ajax的核心函数,jQuery的其他ajax方法几乎都是基于该方法的。

    上一篇文章我们了解了Baidu ajax(当然是旧版的,还是被简化的……),那么我们想给这个简单的ajax方法添加什么功能呢?

    可链式操作

    既然是jQuery必然先要解决的是链式操作的问题。

    jQuery中的Deferred可以实现异步链式模型实现,Promise对象可以轻易的绑定成功、失败、进行中三种状态的回调函数,然后通过在状态码在来回调不同的函数就行了。

    但仅仅返回一个promise没什么用

    promise只能处理异步,我们需要返回一个更有用的东西。

    jqXHR就是这样的东西,实际上他是一个山寨版的XHR对象。

    这样我们就可以扩展一下XHR对象的功能, 提供一定的容错处理,暴露给外部提供一些方法。

    // 赝品xhr,或者说山寨xhr……╮(╯▽╰)╭
    // 为了能够实现链式操作
    // 顺便扩展一下xhr对象功能
    // 还有提供一定的容错处理
    jqXHR = {
        // 准备状态
        readyState: 0,
    
        // 如果需要,创建一个响应头参数的表
        getResponseHeader: function( key ) {
            var match;
            // 如果状态为2,状态2表示ajax完成
            if ( state === 2 ) {
                // 如果没有相应头
                if ( !responseHeaders ) {
                    // 相应头设空
                    responseHeaders = {};
                    while ( (match = rheaders.exec( responseHeadersString )) ) {
                        // 组装相应头
                        responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                    }
                }
                // 响应头对应的key的值
                match = responseHeaders[ key.toLowerCase() ];
            }
            // 返回
            return match == null ? null : match;
        },
    
        // 返回响应头字符串
        getAllResponseHeaders: function() {
            // 看看是否接收到了,接收到直接返回,否则为null
            return state === 2 ? responseHeadersString : null;
        },
    
        // 设置请求头
        setRequestHeader: function( name, value ) {
            var lname = name.toLowerCase();
            // 如果state不为0
            if ( !state ) {
                // 如果requestHeadersNames[ lname ]不为空,
                // 则requestHeadersNames[ lname ]不变,name设置为该值
                // 否则,requestHeadersNames[ lname ]不空,
                // 则requestHeadersNames[ lname ]设置为name,
                // 该映射关系用于避免用户大小写书写错误之类的问题,容错处理
                name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
                // 现在的name是对的,或者是第一次设置这个name,不需要容错
                // 设置请求头对应值
                requestHeaders[ name ] = value;
            }
            return this;
        },
    
        // 重写相应头content-type
        overrideMimeType: function( type ) {
            if ( !state ) {
                s.mimeType = type;
            }
            return this;
        },
    
        // 对应状态的回调函数集
        statusCode: function( map ) {
            var code;
            // 如果map存在,准备组装
            if ( map ) {
                // 如果状态小于2,表示旧的回调可能还没有用到
                if ( state < 2 ) {
                    // 遍历map里面的所有code
                    for ( code in map ) {
                        // 用类似链表的方式添加,以保证旧的回调依然存在
                        statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
                    }
                // 状态大于2,证明已经完成了
                } else {
                    // 无论Deferred成功还是失败都执行当前状态回调
                    jqXHR.always( map[ jqXHR.status ] );
                }
            }
            return this;
        },
    
        // 中断请求
        abort: function( statusText ) {
            var finalText = statusText || strAbort;
            // 可以先理解成XHR对象,当然这也不是真正的XHR对象
            if ( transport ) {
                transport.abort( finalText );
            }
            // 调用done,表示干完了
            done( 0, finalText );
            return this;
        }
    };

    可是这还没有链式啊!

    怎么把jqXHR变成一个Promise呢?

    让一个对象拥有另一个对象的方法,大家会想到什么呢?

    继承?

    不,直接插上去就好了……这里就体现了Javascript“易插拔”的特点……自己乱起的……囧rz……

    应该说动态、弱类的特点。

    什么意思?就是将Promise上的方法插到jqXHR对象上就行了。

        // 在jqXHR粘上promise的所有方法,此时jqXHR就很像一个promise了
        // 实际上就是用jQuery.extend(jqXHR, promise)而已
        // jqXHR刚山寨了xhr对象,又开始山寨promise对象了
        // 顺便把jqXHR的complete绑上completeDeferred.add
        // 意思是jqXHR状态完成后调用completeDeferred这个Callbacks列队
        // 话说promise里面并没有complete这个方法
        // 后面我们可以看到,作者大人又要给jqXHR插上complete、success、error方法
        // Javascript就是这样简单,即插即用……囧rz
        deferred.promise( jqXHR ).complete = completeDeferred.add;
        // 绑定jqXHR.success为promise里面的done方法
        jqXHR.success = jqXHR.done;
        // 绑定jqXHR.error为promise里面的fail方法
        jqXHR.error = jqXHR.fail;

    这样子直接插上去在强类语言中是做不到的,当然也可以用组合模式来实现一个对象拥有多个对象的方法,但是却无法动态的去给对象添加各种方法。

    强类语言会限制对象一辈子只能“会”那么几种“方法”,Javascript的对象可以在生命周期内随意“学习”各种“方法”。

    所以说,强类语言和Javascript的对象模型是有差别的,将设计模式生搬硬套进Javascript或许是错误的。

    我们再举个例子,比如如果用所谓的Javascript组合模式来重写上面的代码可能会是这样的:

    // 定义局部变量deferred和completeDeferred
    function JQXHR(){
        // 定义jqXHR的属性和方法
        for(i in deferred.promise){
            this[i] = this.promise[i];
        }
        this.complete = completeDeferred.add;
        this.success = this.done;
        this.error = this.done
    }
    
    var jqXHR = new JQXHR();

    大家觉得哪个好呢?

    变成上面的样子主要是因为要解决下面两个问题:

    1. jqXHR由于是暴露在外的,他不能包含deferred和completeDeferred,不允许用户在外部操作这两个对象的状态。
    2. deferred和completeDeferred也不能成为jqXHR的私有变量,因为还要更具ajax事件触发。

    提供全局事件,使得UI可以根据ajax状态进行改变

    这里主要利用了jQuery.event.trigger和jQuery.fn.trigger模拟发事件。

        // 如果需要,而且全局事件没有被触发过
        if ( fireGlobals && jQuery.active++ === 0 ) {
            // 则通过jQuery.event.trigger模拟触发
            jQuery.event.trigger("ajaxStart");
        }

    当ajax开始时模拟全局事件,ajaxStart。

            // 如果需要,对特定对象触发全局事件ajaxSend
            if ( fireGlobals ) {
                globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
            }

    ajax发送消息,触发ajaxSend。

            // 如果需要触发全局事件
            if ( fireGlobals ) {
                // 对指定元素触发事件ajaxSuccess或者ajaxError
                globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
                    [ jqXHR, s, isSuccess ? success : error ] );
            }
    
            // 回调完成后的Callbacks队列
            completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
    
            // 如果需要触发全局事件
            if ( fireGlobals ) {
                // 对指定元素触发事件ajaxComplete
                globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
                // 该ajax触发完毕,标记active减1,如果为0,证明所有ajax结束
                if ( !( --jQuery.active ) ) {
                    // 触发ajaxStop事件
                    jQuery.event.trigger("ajaxStop");
                }
            }    

    结束时候触发ajaxSuccess或ajaxError,再出发ajaxComplete,如果全部ajax结束则触发ajaxStop。

    是否允许使用缓存数据

        // 如果不需要content
        // 看看需不需要加其他信息
        if ( !s.hasContent ) {
    
            // 如果data存在,那么已经序列化
            if ( s.data ) {
                // 添加到cacheURL中
                cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
                // 删除掉则后面就不会被发送出去了
                delete s.data;
            }
    
            // 看看是否需要避免数据从缓存中读取
            if ( s.cache === false ) {
            
                s.url = rts.test( cacheURL ) ?
    
                    // 如果已经有_参数,那么设置他的值
                    cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
    
                    // 否则添加一个_ = xxx在URL后面
                    cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
            }
        }

    通过给地址附加参数_=xxx来避免缓存。

        // 看看需不需要设置If-Modified-Since和If-None-Match头信息
        if ( s.ifModified ) {
            if ( jQuery.lastModified[ cacheURL ] ) {
                jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
            }
            if ( jQuery.etag[ cacheURL ] ) {
                jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
            }
        }

    以及设置If-Modified-Since和If-None-Match头信息。

        // 看看是否需要缓存置If-Modified-Since和If-None-Match头
        if ( s.ifModified ) {
            // 取得Last-Modified
            modified = jqXHR.getResponseHeader("Last-Modified");
            // 如果Last-Modified存在
            if ( modified ) {
                // 在jQuery.lastModified[cacheURL]保存Last-Modified
                jQuery.lastModified[ cacheURL ] = modified;
            }
            // 取得etag
            modified = jqXHR.getResponseHeader("etag");
            // 如果etag存在
            if ( modified ) {
                // 在jQuery.etag[cacheURL]缓存etag
                jQuery.etag[ cacheURL ] = modified;
            }
        }

    缓存If-Modified-Since和If-None-Match头信息。

    设置超时

            // 如果是异步,并且设置了超时
            if ( s.async && s.timeout > 0 ) {
                // 设置超时
                timeoutTimer = setTimeout(function() {
                    jqXHR.abort("timeout");
                }, s.timeout );
            }

    主要功能分析完了,完整备注代码见下。 

    完整备注

    jQuery.ajax = function( url, options ) {
    
        // 如果url是一个obj,模拟1.5版本以前的方法
        if ( typeof url === "object" ) {
            options = url;
            url = undefined;
        }
    
        // 设置options
        options = options || {};
    
        var transport,
            // 缓存cacheURL
            cacheURL,
            // 响应头
            responseHeadersString,
            responseHeaders,
            // 超时控制器
            timeoutTimer,
            // 跨域判定变量
            parts,
            // 是否需要触发全局事件
            fireGlobals,
            // 循环变量
            i,
            // 通过jQuery.ajaxSetup改造参数对象
            s = jQuery.ajaxSetup( {}, options ),
            // 回调指定上下文,也就是他的this
            callbackContext = s.context || s,
            // 全局事件中的相应函数的指定上下文
            // 有s.context,且是DOM节点,或者jQuery收集器
            globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
                // 通过jQuery包装
                jQuery( callbackContext ) :
                // 否则为jQuery.event
                jQuery.event,
            // 新建一个deferred
            deferred = jQuery.Deferred(),
            // deferred完成后的Callbacks队列
            completeDeferred = jQuery.Callbacks("once memory"),
            // 对应状态的回调函数集
            statusCode = s.statusCode || {},
            // 请求头
            requestHeaders = {},
            requestHeadersNames = {},
            // 包装类jqXHR的状态
            state = 0,
            // 默认中断消息
            strAbort = "canceled",
            // 赝品xhr,或者说山寨xhr……╮(╯▽╰)╭
            // 为了能够实现链式操作
            // 顺便扩展一下xhr对象功能
            // 还有提供一定的容错处理
            jqXHR = {
                // 准备状态
                readyState: 0,
    
                // 如果需要,创建一个响应头参数的表
                getResponseHeader: function( key ) {
                    var match;
                    // 如果状态为2,状态2表示ajax完成
                    if ( state === 2 ) {
                        // 如果没有相应头
                        if ( !responseHeaders ) {
                            // 相应头设空
                            responseHeaders = {};
                            while ( (match = rheaders.exec( responseHeadersString )) ) {
                                // 组装相应头
                                responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                            }
                        }
                        // 响应头对应的key的值
                        match = responseHeaders[ key.toLowerCase() ];
                    }
                    // 返回
                    return match == null ? null : match;
                },
    
                // 返回响应头字符串
                getAllResponseHeaders: function() {
                    // 看看是否接收到了,接收到直接返回,否则为null
                    return state === 2 ? responseHeadersString : null;
                },
    
                // 缓存请求头
                setRequestHeader: function( name, value ) {
                    var lname = name.toLowerCase();
                    // 如果state不为0
                    if ( !state ) {
                        // 如果requestHeadersNames[ lname ]不为空,
                        // 则requestHeadersNames[ lname ]不变,name设置为该值
                        // 否则,requestHeadersNames[ lname ]不空,
                        // 则requestHeadersNames[ lname ]设置为name,
                        // 该映射关系用于避免用户大小写书写错误之类的问题,容错处理
                        name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
                        // 现在的name是对的,或者是第一次设置这个name,不需要容错
                        // 设置请求头对应值
                        requestHeaders[ name ] = value;
                    }
                    return this;
                },
    
                // 重写相应头content-type
                overrideMimeType: function( type ) {
                    if ( !state ) {
                        s.mimeType = type;
                    }
                    return this;
                },
    
                // 对应状态的回调函数集
                statusCode: function( map ) {
                    var code;
                    // 如果map存在,准备组装
                    if ( map ) {
                        // 如果状态小于2,表示旧的回调可能还没有用到
                        if ( state < 2 ) {
                            // 遍历map里面的所有code
                            for ( code in map ) {
                                // 用类似链表的方式添加,以保证旧的回调依然存在
                                statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
                            }
                        // 状态大于2,证明已经完成了
                        } else {
                            // 无论Deferred成功还是失败都执行当前状态回调
                            jqXHR.always( map[ jqXHR.status ] );
                        }
                    }
                    return this;
                },
    
                // 中断请求
                abort: function( statusText ) {
                    var finalText = statusText || strAbort;
                    // 可以先理解成XHR对象,当然这也不是真正的XHR对象
                    if ( transport ) {
                        transport.abort( finalText );
                    }
                    // 调用done,表示干完了
                    done( 0, finalText );
                    return this;
                }
            };
    
        // 在jqXHR粘上promise的所有方法,此时jqXHR就很像一个promise了
        // 实际上就是用jQuery.extend(jqXHR, promise)而已
        // jqXHR刚山寨了xhr对象,又开始山寨promise对象了
        // 顺便把jqXHR的complete绑上completeDeferred.add
        // 意思是jqXHR状态完成后调用completeDeferred这个Callbacks列队
        // 话说promise里面并没有complete这个方法
        // 后面我们可以看到,作者大人又要给jqXHR插上complete、success、error方法
        // Javascript就是这样简单,即插即用……囧rz
        deferred.promise( jqXHR ).complete = completeDeferred.add;
        // 绑定jqXHR.success为promise里面的done方法
        jqXHR.success = jqXHR.done;
        // 绑定jqXHR.error为promise里面的fail方法
        jqXHR.error = jqXHR.fail;
    
        // 确定url参数,否则用当前地址。将地址的#号后面的所有东西去掉
        // 比如http://127.0.0.1#main,去掉这个#main
        // 如果开头是//,及数据传输协议没有,那么用当前页面的数据传输协议替换
        // 比如//127.0.0.1,变成http://127.0.0.1
        s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
    
        // 定义type,向后兼容
        s.type = options.method || options.type || s.method || s.type;
    
        // 取出数据类型列表
        // 没有则为"*",
        // 有则认为是用空格分隔的字符串,将其变成数组
        s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
    
        // 当协议、主机、端口和当前不匹配时,证明这是一个跨域请求
        if ( s.crossDomain == null ) {
            // 分隔当前url
            parts = rurl.exec( s.url.toLowerCase() );
            // 判定是否是跨域
            s.crossDomain = !!( parts &&
                ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
                    ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                        ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
            );
        }
    
        // 如果data已经是一个字符串了,那么就不用转换了
        if ( s.data && s.processData && typeof s.data !== "string" ) {
            // 序列化
            s.data = jQuery.param( s.data, s.traditional );
        }
    
        // 预过滤
        inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
    
        // 如果请求被prefilter终止,则退出
        if ( state === 2 ) {
            return jqXHR;
        }
    
        // 看看需不需要触发全局事件
        fireGlobals = s.global;
    
        // 如果需要,而且全局事件没有被触发过
        if ( fireGlobals && jQuery.active++ === 0 ) {
            // 则通过jQuery.event.trigger模拟触发
            jQuery.event.trigger("ajaxStart");
        }
    
        // 将类型大写
        s.type = s.type.toUpperCase();
    
        // 判断需不需要设置content
        s.hasContent = !rnoContent.test( s.type );
    
        // 缓存URL,用来在之后设置If-Modified-Since和If-None-Match
        cacheURL = s.url;
    
        // 如果不需要content
        // 看看需不需要加其他信息
        if ( !s.hasContent ) {
    
            // 如果data存在,那么已经序列化
            if ( s.data ) {
                // 添加到cacheURL中
                cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
                // 删除掉则后面就不会被发送出去了
                delete s.data;
            }
    
            // 看看是否需要避免数据从缓存中读取
            if ( s.cache === false ) {
            
                s.url = rts.test( cacheURL ) ?
    
                    // 如果已经有_参数,那么设置他的值
                    cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
    
                    // 否则添加一个_ = xxx在URL后面
                    cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
            }
        }
    
        // 看看需不需要设置If-Modified-Since和If-None-Match头信息
        if ( s.ifModified ) {
            if ( jQuery.lastModified[ cacheURL ] ) {
                jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
            }
            if ( jQuery.etag[ cacheURL ] ) {
                jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
            }
        }
    
        // 如果数据需要被发送,设置正确的头
        if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
            jqXHR.setRequestHeader( "Content-Type", s.contentType );
        }
    
        // 根据dataType,设置一个Accept头
        jqXHR.setRequestHeader(
            "Accept",
            s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
                s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
                s.accepts[ "*" ]
        };
    
        // 遍历s.headers,将其内参数设置入请求头
        for ( i in s.headers ) {
            jqXHR.setRequestHeader( i, s.headers[ i ] );
        }
    
        // 通过beforeSend检查是否需要发送
        if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
            // 终止
            return jqXHR.abort();
        }
    
        // 此时abort函数不是取消ajax,而是中断了ajax
        strAbort = "abort";
    
        // 在jqXHR上绑定成功、错误、完成回调函数
        for ( i in { success: 1, error: 1, complete: 1 } ) {
            jqXHR[ i ]( s[ i ] );
        }
    
        // 得到transport
        transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
    
        // 如果没有,自动终止
        if ( !transport ) {
            done( -1, "No Transport" );
        } else {
            // 否则,设置reayState为1
            jqXHR.readyState = 1;
            
            // 如果需要,对特定对象触发全局事件ajaxSend
            if ( fireGlobals ) {
                globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
            }
            // 如果是异步,并且设置了超时
            if ( s.async && s.timeout > 0 ) {
                // 设置超时
                timeoutTimer = setTimeout(function() {
                    jqXHR.abort("timeout");
                }, s.timeout );
            }
    
            try {
                // 设置state为1
                state = 1;
                // 开始发送
                transport.send( requestHeaders, done );
            } catch ( e ) {
                // 截获错误,如果ajax未完成
                if ( state < 2 ) {
                    done( -1, e );
                // 完成了就直接抛出错误
                } else {
                    throw e;
                }
            }
        }
    
        // 完成时的回调函数
        function done( status, nativeStatusText, responses, headers ) {
            var isSuccess, success, error, response, modified,
                statusText = nativeStatusText;
    
            // 如果已经调用过该函数,直接退出
            if ( state === 2 ) {
                return;
            }
    
            // 设置现在状态已完成
            state = 2;
    
            // 清除超时设置
            if ( timeoutTimer ) {
                clearTimeout( timeoutTimer );
            }
            // 不管jqXHR对象要被用到何时,
            // 释放transport的引用使得他可以先被垃圾回收
            transport = undefined;
    
            // 缓存响应头
            responseHeadersString = headers || "";
    
            // 设置readyState
            jqXHR.readyState = status > 0 ? 4 : 0;
    
            // 得到响应数据
            if ( responses ) {
                // 通过ajaxHandleResponses处理数据
                response = ajaxHandleResponses( s, jqXHR, responses );
            }
    
            // If successful, handle type chaining
            // 如果成功
            if ( status >= 200 && status < 300 || status === 304 ) {
    
                // 看看是否需要缓存If-Modified-Since和If-None-Match头
                if ( s.ifModified ) {
                    // 取得Last-Modified
                    modified = jqXHR.getResponseHeader("Last-Modified");
                    // 如果Last-Modified存在
                    if ( modified ) {
                        // 在jQuery.lastModified[cacheURL]保存Last-Modified
                        jQuery.lastModified[ cacheURL ] = modified;
                    }
                    // 取得etag
                    modified = jqXHR.getResponseHeader("etag");
                    // 如果etag存在
                    if ( modified ) {
                        // 在jQuery.etag[cacheURL]缓存etag
                        jQuery.etag[ cacheURL ] = modified;
                    }
                }
    
                // 如果没有修改
                if ( status === 304 ) {
                    // 设置成功
                    isSuccess = true;
                    // 设置状态为notmodified
                    statusText = "notmodified";
    
                // 否则得到必要的数据
                } else {
                    isSuccess = ajaxConvert( s, response );
                    statusText = isSuccess.state;
                    success = isSuccess.data;
                    error = isSuccess.error;
                    isSuccess = !error;
                }
            // 如果失败
            } else {
                // 从statusText获取error状态
                // 在设置statusText成"error"
                // 并改变status为0
                error = statusText;
                if ( status || !statusText ) {
                    statusText = "error";
                    if ( status < 0 ) {
                        status = 0;
                    }
                }
            }
    
            // 开始设置山寨xhr对象jqXHR的status和statusText
            jqXHR.status = status;
            jqXHR.statusText = ( nativeStatusText || statusText ) + "";
    
            // 根据成功还是失败,对deferred进行回调
            if ( isSuccess ) {
                deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );    
            } else {
                deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
            }
    
            // 根据目前statusCode回调
            jqXHR.statusCode( statusCode );
            statusCode = undefined;
    
            // 如果需要触发全局事件
            if ( fireGlobals ) {
                // 对指定元素触发事件ajaxSuccess或者ajaxError
                globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
                    [ jqXHR, s, isSuccess ? success : error ] );
            }
    
            // 回调完成后的Callbacks队列
            completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
    
            // 如果需要触发全局事件
            if ( fireGlobals ) {
                // 对指定元素触发事件ajaxComplete
                globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
                // 该ajax触发完毕,标记active减1,如果为0,证明所有ajax结束
                if ( !( --jQuery.active ) ) {
                    // 触发ajaxStop事件
                    jQuery.event.trigger("ajaxStop");
                }
            }
        }
    
        // 返回山寨xhr
        return jqXHR;
    };
  • 相关阅读:
    WAVECOM CDMA Modem 发送短信
    【转】关于正则表达式匹配任意字符(包括换行符)的写法
    MS2000 差异备份 还原
    推荐一款非常适用的弹框 phpcms v9都用的这个!!!!
    正则截取内容
    javascript面向对象编程实现
    一次 全部删除MSSQL数据库用户表,存储过程
    【转】 jQuery图片预加载+等比例缩放
    多种多样的Loading特效
    关于图片轮播的几种思路
  • 原文地址:https://www.cnblogs.com/justany/p/2872333.html
Copyright © 2011-2022 走看看