资讯专栏INFORMATION COLUMN

jQuery ajax 源码分析二之ajax主函数

godlong_X / 2209人阅读

摘要:上文我们已经介绍了的几个副函数和,本文主要介绍主函数的内部实现我们一般调用有三种写法第一种写法第二种写法第三种写法,也就是的写法第一种和第二种仅仅是的位置不同,内部会判断传入的第一个参数是否是对象来进行判断使用的写法,需要转换成的写法内部

上文我们已经介绍了ajax 的几个副函数ajaxPrefilterajaxTransport ,本文主要介绍ajax 主函数的内部实现

我们一般调用ajax 有三种写法

</>复制代码

  1. // 第一种写法
  2. $.ajax({
  3. url:url,
  4. data:{...},
  5. ...
  6. success:function(){},
  7. error:function(){}
  8. })
  9. // 第二种写法
  10. $.ajax(url, {
  11. data:{...},
  12. ...
  13. success:function(){},
  14. error:function(){}
  15. })
  16. // 第三种写法,也就是deferred的写法
  17. $.ajax(url, {
  18. data:{...},
  19. ...
  20. }).done().fail();

第一种和第二种仅仅是url 的位置不同,ajax 内部会判断传入ajax 的第一个参数是否是对象来进行判断

</>复制代码

  1. // If url is an object, simulate pre-1.5 signature
  2. // 使用 $.ajax({url:url,data:data}) 的写法,需要转换成 $.ajax(url, {data:data}); 的写法
  3. if ( typeof url === "object" ) {
  4. options = url;
  5. url = undefined;
  6. }

ajax 内部通过新增jqXHR 对象来增加ajax 的功能,例如statusCode 根据ajax 中设置相应的http 状态码对象的函数来实现当响应的状态码对应到设置的状态码时,触发相应的函数

</>复制代码

  1. // Fake xhr
  2. // 模拟出来的 ajax ,增加原生ajax的功能
  3. jqXHR = {
  4. readyState: 0,
  5. // Builds headers hashtable if needed
  6. getResponseHeader: function( key ) {},
  7. // Raw string
  8. getAllResponseHeaders: function() {},
  9. // Caches the header
  10. setRequestHeader: function( name, value ) { },
  11. // Overrides response content-type header
  12. overrideMimeType: function( type ) {},
  13. // Status-dependent callbacks
  14. // 状态码对应后,如何触发相关的函数
  15. statusCode: function( map ) {},
  16. // Cancel the request
  17. // ajax时间超过timeout响应的时间时,就触发 abort 函数
  18. abort: function( statusText ) {}
  19. };

ajax 的第三种写法,就是通过将jqXHR 添加到deferred 中,所以deferred 的所有方法,ajax 也可以同样使用

</>复制代码

  1. // Deferreds
  2. // ajax 可以使用两种方式来写,链式调用的话,就需要使用 deferreds ,这样就能够触发是否是 done还是其他状态来触发相应的事件
  3. deferred = jQuery.Deferred(),
  4. completeDeferred = jQuery.Callbacks("once memory"),
  5. // Attach deferreds
  6. // 把deferred的方法,添加到 模拟的 jqXHR 中
  7. deferred.promise( jqXHR ).complete = completeDeferred.add;
  8. jqXHR.success = jqXHR.done;
  9. jqXHR.error = jqXHR.fail;

上一文中介绍了ajaxaddPrefilters 函数,在ajax 发送数据之前,会通过inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); 调用所有的预处理函数,发送数据时,通过transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); 来发送数据,transports 内会根据是否跨域选择哪个send/abort 函数,返回的结果,通过done() 函数进行处理

</>复制代码

  1. // Main method
  2. ajax: function( url, options ) {
  3. // If url is an object, simulate pre-1.5 signature
  4. // 使用 $.ajax({url:url,data:data}) 的写法,需要转换成 $.ajax(url, {data:data}); 的写法
  5. if ( typeof url === "object" ) {
  6. options = url;
  7. url = undefined;
  8. }
  9. // Force options to be an object
  10. options = options || {};
  11. var transport,
  12. // URL without anti-cache param
  13. cacheURL,
  14. // Response headers
  15. responseHeadersString,
  16. responseHeaders,
  17. // timeout handle
  18. timeoutTimer,
  19. // Cross-domain detection vars
  20. parts,
  21. // To know if global events are to be dispatched
  22. fireGlobals,
  23. // Loop variable
  24. i,
  25. // Create the final options object
  26. s = jQuery.ajaxSetup( {}, options ), // 通过把options的参数放到一个新对象中,这样就所有的参数都只会影响当前的ajax
  27. // Callbacks context
  28. callbackContext = s.context || s, // 执行的上下文,根据命名,应该是回调函数的上下文
  29. // Context for global events is callbackContext if it is a DOM node or jQuery collection
  30. // 这里应该是触发 ajax 的全局事件,如果有设置上下文context,那么就能够使用 jQuery( callbackContext ) ,也就是某个元素来绑定
  31. // 否则,就使用默认的 jQuery底层的event来调用
  32. globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
  33. jQuery( callbackContext ) :
  34. jQuery.event,
  35. // Deferreds
  36. // ajax 可以使用两种方式来写,链式调用的话,就需要使用 deferreds ,这样就能够触发是否是 done还是其他状态来触发相应的事件
  37. deferred = jQuery.Deferred(),
  38. completeDeferred = jQuery.Callbacks("once memory"),
  39. // Status-dependent callbacks
  40. // 状态码对应的回调操作,和success 事件同级别 statusCode: {404:function(){alert("页面不存在")}
  41. statusCode = s.statusCode || {},
  42. // Headers (they are sent all at once)
  43. requestHeaders = {},
  44. requestHeadersNames = {},
  45. // The jqXHR state
  46. state = 0,
  47. // Default abort message
  48. strAbort = "canceled",
  49. // Fake xhr
  50. // 模拟出来的 ajax ,原生的还不够强大
  51. jqXHR = {
  52. readyState: 0,
  53. // Builds headers hashtable if needed
  54. getResponseHeader: function( key ) {
  55. var match;
  56. if ( state === 2 ) {
  57. if ( !responseHeaders ) {
  58. responseHeaders = {};
  59. while ( (match = rheaders.exec( responseHeadersString )) ) {
  60. responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
  61. }
  62. }
  63. match = responseHeaders[ key.toLowerCase() ];
  64. }
  65. return match == null ? null : match;
  66. },
  67. // Raw string
  68. getAllResponseHeaders: function() {
  69. return state === 2 ? responseHeadersString : null;
  70. },
  71. // Caches the header
  72. setRequestHeader: function( name, value ) {
  73. var lname = name.toLowerCase();
  74. if ( !state ) {
  75. name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
  76. requestHeaders[ name ] = value;
  77. }
  78. return this;
  79. },
  80. // Overrides response content-type header
  81. overrideMimeType: function( type ) {
  82. if ( !state ) {
  83. s.mimeType = type;
  84. }
  85. return this;
  86. },
  87. // Status-dependent callbacks
  88. // 状态码对应后,如何触发相关的函数
  89. statusCode: function( map ) {
  90. var code;
  91. if ( map ) {
  92. if ( state < 2 ) {
  93. for ( code in map ) {
  94. // Lazy-add the new callback in a way that preserves old ones
  95. statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
  96. }
  97. } else {
  98. // Execute the appropriate callbacks
  99. jqXHR.always( map[ jqXHR.status ] );
  100. }
  101. }
  102. return this;
  103. },
  104. // Cancel the request
  105. // ajax时间超过timeout响应的时间时,就触发 abort 函数
  106. abort: function( statusText ) {
  107. var finalText = statusText || strAbort;
  108. if ( transport ) {
  109. transport.abort( finalText );
  110. }
  111. done( 0, finalText );
  112. return this;
  113. }
  114. };
  115. // Attach deferreds
  116. // 把deferred的方法,添加到 模拟的 jqXHR 中
  117. deferred.promise( jqXHR ).complete = completeDeferred.add;
  118. jqXHR.success = jqXHR.done;
  119. jqXHR.error = jqXHR.fail;
  120. // Remove hash character (#7531: and string promotion)
  121. // Add protocol if not provided (prefilters might expect it)
  122. // Handle falsy url in the settings object (#10093: consistency with old signature)
  123. // We also use the url parameter if available
  124. // 把地址中 hash 设置为空,因为在ajax中,hash值是没有用处的,如果地址中有//,就替换成 http:// 这样的标准写法
  125. s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
  126. .replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); // 如果地址写成 //data.php 就会转换成 http://data.php
  127. // Alias method option to type as per ticket #12004
  128. // get/post 也可以使用 method 或者是 type
  129. s.type = options.method || options.type || s.method || s.type;
  130. // Extract dataTypes list
  131. // text html json 如果这样写 dataType,就需要转换成一个集合
  132. s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
  133. // A cross-domain request is in order when we have a protocol:host:port mismatch
  134. // 检测是否跨域
  135. if ( s.crossDomain == null ) {
  136. parts = rurl.exec( s.url.toLowerCase() );
  137. s.crossDomain = !!( parts &&
  138. ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
  139. ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
  140. ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
  141. );
  142. }
  143. // Convert data if not already a string
  144. // 处理使用 ajax 传递的数据
  145. if ( s.data && s.processData && typeof s.data !== "string" ) {
  146. s.data = jQuery.param( s.data, s.traditional );
  147. }
  148. // Apply prefilters,
  149. // 这时,触发所有的预处理函数
  150. // s 就是 ajax 所有的参数, options 开发者传进来的参数
  151. inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
  152. // If request was aborted inside a prefilter, stop there
  153. // 如果在 prefilter 里面的请求就被终止了,就放回当前对象
  154. if ( state === 2 ) {
  155. return jqXHR;
  156. }
  157. // We can fire global events as of now if asked to
  158. fireGlobals = s.global;
  159. // Watch for a new set of requests
  160. // 全局事件触发,这时候没有使用具体的元素,直接使用的默认是 document 触发 $(document).on("ajaxStart",function(){}),而不是$("div").on("ajaxStart",function(){})
  161. if ( fireGlobals && jQuery.active++ === 0 ) {
  162. jQuery.event.trigger("ajaxStart");
  163. }
  164. // Uppercase the type
  165. s.type = s.type.toUpperCase();
  166. // Determine if request has content
  167. // rnoContent 为 get/head ,就是使用 ajax 的时候,把数据加到网址的后面
  168. s.hasContent = !rnoContent.test( s.type );
  169. // Save the URL in case we"re toying with the If-Modified-Since
  170. // and/or If-None-Match header later on
  171. cacheURL = s.url;
  172. // More options handling for requests with no content
  173. if ( !s.hasContent ) {
  174. // If data is available, append data to url
  175. if ( s.data ) {
  176. cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
  177. // #9682: remove data so that it"s not used in an eventual retry
  178. delete s.data;
  179. }
  180. // Add anti-cache in url if needed
  181. // 如果不需要缓存,就会在url的后面加上时间戳,这样每次请求都是一次新的请求
  182. if ( s.cache === false ) {
  183. s.url = rts.test( cacheURL ) ?
  184. // If there is already a "_" parameter, set its value
  185. cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
  186. // Otherwise add one to the end
  187. cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
  188. }
  189. }
  190. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  191. // 如果数据没有变,就使用缓存的数据
  192. if ( s.ifModified ) {
  193. if ( jQuery.lastModified[ cacheURL ] ) {
  194. jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
  195. }
  196. if ( jQuery.etag[ cacheURL ] ) {
  197. jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
  198. }
  199. }
  200. // Set the correct header, if data is being sent
  201. if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
  202. jqXHR.setRequestHeader( "Content-Type", s.contentType );
  203. }
  204. // Set the Accepts header for the server, depending on the dataType
  205. jqXHR.setRequestHeader(
  206. "Accept",
  207. s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
  208. s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
  209. s.accepts[ "*" ]
  210. );
  211. // Check for headers option
  212. for ( i in s.headers ) {
  213. jqXHR.setRequestHeader( i, s.headers[ i ] );
  214. }
  215. // Allow custom headers/mimetypes and early abort
  216. // 如果停止 ajax 事件,就直接调用 abort 事件
  217. if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
  218. // Abort if not done already and return
  219. return jqXHR.abort();
  220. }
  221. // aborting is no longer a cancellation
  222. strAbort = "abort";
  223. // Install callbacks on deferreds
  224. // 把 success 这些事件映射到 jqXHR 中
  225. for ( i in { success: 1, error: 1, complete: 1 } ) {
  226. jqXHR[ i ]( s[ i ] );
  227. }
  228. // Get transport
  229. // 触发回调,ajax对象的send/abort方法,如果跨域,就在url内创建script的方法,如果不跨域,就使用通过原生ajax封装好的send/abort方法
  230. transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
  231. // If no transport, we auto-abort
  232. if ( !transport ) {
  233. done( -1, "No Transport" );
  234. } else {
  235. jqXHR.readyState = 1;
  236. // Send global event
  237. if ( fireGlobals ) {
  238. globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
  239. }
  240. // Timeout
  241. // 如果请求的时间大于设置的timeout,就调用 abort 函数
  242. if ( s.async && s.timeout > 0 ) {
  243. timeoutTimer = setTimeout(function() {
  244. jqXHR.abort("timeout");
  245. }, s.timeout );
  246. }
  247. try {
  248. state = 1;
  249. // 调用transport封装好的 send 发送
  250. transport.send( requestHeaders, done );
  251. } catch ( e ) {
  252. // Propagate exception as error if not done
  253. if ( state < 2 ) {
  254. done( -1, e );
  255. // Simply rethrow otherwise
  256. } else {
  257. throw e;
  258. }
  259. }
  260. }
  261. // Callback for when everything is done
  262. function done( status, nativeStatusText, responses, headers ) {
  263. var isSuccess, success, error, response, modified,
  264. statusText = nativeStatusText;
  265. // Called once
  266. if ( state === 2 ) {
  267. return;
  268. }
  269. // State is "done" now
  270. state = 2;
  271. // Clear timeout if it exists
  272. if ( timeoutTimer ) {
  273. clearTimeout( timeoutTimer );
  274. }
  275. // Dereference transport for early garbage collection
  276. // (no matter how long the jqXHR object will be used)
  277. transport = undefined;
  278. // Cache response headers
  279. responseHeadersString = headers || "";
  280. // Set readyState
  281. jqXHR.readyState = status > 0 ? 4 : 0;
  282. // Determine if successful
  283. isSuccess = status >= 200 && status < 300 || status === 304;
  284. // Get response data
  285. // 然后jquery进行处理,得到合适的输出,可以通过使用 console.log来打印出 ajax 返回的数据,来看出是怎么处理的数据
  286. // console.log(responses)
  287. if ( responses ) {
  288. response = ajaxHandleResponses( s, jqXHR, responses );
  289. }
  290. // console.log(responses)
  291. // Convert no matter what (that way responseXXX fields are always set)
  292. response = ajaxConvert( s, response, jqXHR, isSuccess );
  293. // console.log(responses)
  294. // If successful, handle type chaining
  295. if ( isSuccess ) {
  296. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  297. // 是否需要使用缓存中的数据
  298. if ( s.ifModified ) {
  299. modified = jqXHR.getResponseHeader("Last-Modified");
  300. if ( modified ) {
  301. jQuery.lastModified[ cacheURL ] = modified;
  302. }
  303. modified = jqXHR.getResponseHeader("etag");
  304. if ( modified ) {
  305. jQuery.etag[ cacheURL ] = modified;
  306. }
  307. }
  308. // if no content
  309. if ( status === 204 || s.type === "HEAD" ) {
  310. statusText = "nocontent";
  311. // if not modified
  312. } else if ( status === 304 ) {
  313. statusText = "notmodified";
  314. // If we have data, let"s convert it
  315. } else {
  316. statusText = response.state;
  317. success = response.data;
  318. error = response.error;
  319. isSuccess = !error;
  320. }
  321. } else {
  322. // We extract error from statusText
  323. // then normalize statusText and status for non-aborts
  324. error = statusText;
  325. if ( status || !statusText ) {
  326. statusText = "error";
  327. if ( status < 0 ) {
  328. status = 0;
  329. }
  330. }
  331. }
  332. // Set data for the fake xhr object
  333. jqXHR.status = status;
  334. jqXHR.statusText = ( nativeStatusText || statusText ) + "";
  335. // Success/Error
  336. // 通过返回的数据是否成功,来触发deferred 的resolveWith和rejectWith函数
  337. if ( isSuccess ) {
  338. deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
  339. } else {
  340. deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
  341. }
  342. // Status-dependent callbacks
  343. // 调用ajax返回的状态码,来触发开发者自己设置的http状态码的函数,
  344. jqXHR.statusCode( statusCode );
  345. statusCode = undefined;
  346. // 如果设置了全局函数,根据ajax是否调用成功,来选择触发ajaxSuccess函数还是ajaxError函数
  347. if ( fireGlobals ) {
  348. globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
  349. [ jqXHR, s, isSuccess ? success : error ] );
  350. }
  351. // Complete
  352. completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
  353. // ajax 调用完成,触发全局函数 ajaxComplete,如果当前页全部的ajax都调用完成,就触发全局函数ajaxStop
  354. if ( fireGlobals ) {
  355. globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
  356. // Handle the global AJAX counter
  357. if ( !( --jQuery.active ) ) {
  358. jQuery.event.trigger("ajaxStop");
  359. }
  360. }
  361. }
  362. // 整个 ajax 调用完后,返回的是 jqXHR 对象,就可以使用deferred的一系列方法 done,fail 等方法了
  363. return jqXHR;
  364. },

待续...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94685.html

相关文章

  • jQuery ajax 源码分析之预处理和分发函数(prefilter/transport)

    摘要:调用的情况下,我们通常用来请求数据的方法有前五种方法,在的实现中,本质上还是在调用第六种方法实现的单纯在源码中看前五个函数,代码量都很少,多一点也就是函数,涉及到了的写法,在调用成功时,对返回的数据使用内部方法进行渲 调用jQuery 的情况下,我们通常用来请求数据的方法有 $(element).load(url, callback) $.get(url, data, callbac...

    he_xd 评论0 收藏0
  • 浅析jQuery整体框架与实现(上)

    摘要:通常的做法是,为它们指定回调函数。请求返回请求返回请求返回异步队列解耦异步任务和回调函数为模块队列模块事件提供基础功能。 前言 jQuery整体框架甚是复杂,也不易读懂,这几日一直在研究这个笨重而强大的框架。jQuery的总体架构可以分为:入口模块、底层模块和功能模块。这里,我们以jquery-1.7.1为例进行分析。 jquery的总体架构 16 (function( window,...

    VEIGHTZ 评论0 收藏0
  • 一篇文章带你尝试拿下js异步

    摘要:单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这决定了它只能是单线程,否则会带来很复杂的同步问题。小结本身是单线程的,并没有异步的特性。当异步函数执行时,回调函数会被压入这个队列。 走在前端的大道上 本篇将自己读过的相关 js异步 的文章中,对自己有启发的章节片段总结在这(会对原文进行删改),会不断丰富提炼总结更新。 概念 JS 是单线程的语言。 单线程就意味着...

    MartinDai 评论0 收藏0
  • Deep in JS - 收藏集 - 掘金

    摘要:今天同学去面试,做了两道面试题全部做错了,发过来给道典型的面试题前端掘金在界中,开发人员的需求量一直居高不下。 排序算法 -- JavaScript 标准参考教程(alpha) - 前端 - 掘金来自《JavaScript 标准参考教程(alpha)》,by 阮一峰 目录 冒泡排序 简介 算法实现 选择排序 简介 算法实现 ... 图例详解那道 setTimeout 与循环闭包的经典面...

    enali 评论0 收藏0
  • Zepto 源码分析 1 - 进入 Zepto

    摘要:选择的理由是一个用于现代浏览器的与大体兼容的库。环境搭建分析环境的搭建仅需要一个常规页面和原始代码一个常规页面打开的首页即可,在开发人员工具中即可使用原始代码本篇分析的代码参照,进入该代码分支中即可。 选择 Zepto 的理由 Zepto is a minimalist JavaScript library for modern browsers with a largely jQue...

    Aklman 评论0 收藏0

发表评论

0条评论

godlong_X

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<