jQuery ajax - 주 함수 분석
이 글 은 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 로 만 듭 니까?
한 상대 에 게 다른 상 대 를 갖 게 하 는 방법 은 무엇 일 까?
상속
아니, 그냥 꽂 으 면 돼.
동태, 약 류 의 특징 을 말 해 야 한다.
무슨 소리 야?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;
이렇게 직접 끼 워 넣 으 면 강 한 언어 에 서 는 할 수 없다. 물론 조합 모드 로 한 대상 이 여러 대상 을 가 진 방법 을 실현 할 수도 있 지만 대상 에 게 여러 가지 방법 을 동적 으로 추가 할 수 는 없다.
강 한 언어 는 대상 이 평생 '할 수 있다' 는 몇 가지 '방법' 만 제한 할 수 있 고 자바 script 의 대상 은 생명 주기 내 에 각종 '방법' 을 마음대로 배 울 수 있다.
그래서 강 한 언어 와 자바 script 의 대상 모델 은 차이 가 있 고 디자인 모델 을 자바 script 에 억지로 끼 워 넣 는 것 은 잘못된 것 일 수 있 습 니 다.
예 를 들 어 자바 script 조합 모드 로 위의 코드 를 다시 쓰 면 다음 과 같 을 수 있 습 니 다.
// 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();
어떤 게 좋 을까요?
위의 모습 으로 변 한 것 은 주로 다음 두 가지 문 제 를 해결 해 야 하기 때문이다.
UI 가 ajax 상태 에 따라 변경 할 수 있 도록 전역 이 벤트 를 제공 합 니 다.
여 기 는 주로 jQuery. event. trigger 와 jQuery. fn. trigger 를 이용 하여 사건 을 모 의 했 습 니 다.
// ,
if ( fireGlobals && jQuery.active++ === 0 ) {
// jQuery.event.trigger
jQuery.event.trigger("ajaxStart");
}
ajax 가 시 작 될 때 전역 이 벤트 를 모 의 합 니 다. ajax Start.
// , ajaxSend
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
ajax 에서 메 시 지 를 보 내 면 ajax Send 를 촉발 합 니 다.
//
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");
}
}
끝 날 때 ajax Success 나 ajax Error 를 촉발 하고 ajax Complete 를 출발 합 니 다. 모든 ajax 가 끝나 면 ajax Stop 을 촉발 합 니 다.
캐 시 데 이 터 를 사용 할 수 있 는 지 여부
// 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;
};
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
jQuery 전후 예이 기사에서는 jquery after() 및 before() 메소드의 예를 볼 것입니다. before() 메서드는 선택한 요소 앞에 지정된 콘텐츠를 삽입합니다. after() 메서드는 선택한 요소 뒤에 지정된 콘텐츠...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.