jQuery ajax —— 主函数分析

系统 1501 0

由于jQuery ajax对Callbacks、Deferred、serialize、event等模块的依赖,建议对这些模块没有认识的朋友看一下 jQuery Callbacks jQuery Deferred jQuery serialize jQuery 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;
};
      
    

 

 

jQuery ajax —— 主函数分析


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论