Deferred 비동기 객체의 jQuery 소스 코드 파싱 방법
우리 작업에서는 간단한 DOM
선택기를 제공하고 와 같은 많은 실용적인 메서드를 캡슐화하기 때문에 <code>jQuery
를 우리 프로젝트의 기본 라이브러리로 선택할 수 있습니다. $.ajax()를 사용하면 xhr
및 xdr
개체를 작동하지 않고도 코드 논리를 직접 작성할 수 있습니다. 더욱 풍부한 점은 ES6
가 기본 지원을 하지 않았던 기간 동안 Promise
개체와 유사한 Deferred
개체를 제공하여 를 지원한다는 것입니다. >done/fail/progress/always
방법과 when
일괄 처리 방법이 프로젝트에 도움이 될 수 있습니다. jQuery
选择做自己项目的基础库,因为其提供了简便的DOM
选择器以及封装了很多实用的方法,比如$.ajax()
,它使得我们不用操作xhr
和xdr
对象,直接书写我们的代码逻辑即可。更为丰富的是它在ES6
没有原生支持的那段时间,提供了Deferred
对象,类似于Promise
对象,支持done/fail/progress/always
方法和when
批处理方法,这可能在项目上帮助过你。
ES6
提供了Promise
对象,但由于它是内置C++
实现的,所以你也没法看它的设计。不如我们通过jQuery
的源码来探究其设计思路,并比较一下两者的区别。本文采用jquey-3.1.2.js
版本,其中英文注释为原版,中文注释为我添加。
jQuery
的ajax
总体设计
jQuery
在内部设置了全局的ajax
参数,在每一个ajax
请求初始化时,用传递的参数与默认的全局参数进行混合,并构建一个jqXHR
对象(提供比原生XHR
更为丰富的方法,同时实现其原生方法),通过传递的参数,来判断其是否跨域、传递的参数类型等,设置好相关头部信息。同时其被初始化为一个内置Deferred
对象用于异步操作(后面讲到),添加done/fail
方法作为回调。同时我们也封装了$.get/$.post
方法来快捷调用$.ajax
方法。
上面提到的Deferred
对象,与ES6的Promise
对象类似,用于更为方便的异步操作,多种回调以及更好的书写方式。提供progress/fail/done
方法,并分别用该对象的notify/reject/resolve
方法触发,可以使用then
方法快速设置三个方法,使用always
添加都会执行的回调,并且提供when
方法支持多个异步操作合并回调。可以追加不同的回调列表,其回调列表是使用内部Callbacks
对象,更方便的按照队列的方式来进行执行。
Callbacks
回调队列对象,用于构建易于操作的回调函数集合,在操作完成后进行执行。支持四种初始化的方式once/unique/memory/stopOnFalse
,分别代表只执行依次、去重、缓存结果、链式调用支持终止。提供fired/locked/disabled
状态值,代表是否执行过、上锁、禁用。提供add/remove/empty/fire/lock/disable
方法操作回调函数队列。
主要涉及到的概念就是这三个,不再做延伸,三个对象的设计代码行数在1200行左右,断断续续看了我一周 (´ཀ`」 ∠) 。我们从这三个倒序开始入手剖析其设计。
jQuery.Callbacks
对象
Callbacks
对象,用于管理回调函数的多用途列表。它提供了六个主要方法:
add
: 向列表中添加回调函数remove
: 移除列表中的回调函数empty
: 清空列表中的回调函数fire
: 依次执行列表中的回调函数lock
: 对列表上锁,禁止一切操作,清除数据,但保留缓存的环境变量(只在memory
参数时有用)disable
: 禁用该回调列表,所有数据清空
在初始化时,支持四个参数,用空格分割:
once
: 该回调列表只执行依次memory
: 缓存执行环境,在添加新回调时执行先执行一次unique
: 去重,每一个函数均不同(指的是引用地址)stopOnFalse
: 在调用中,如果前一个函数返回false
,中断列表的后续执行
我们来看下其实例使用:
let cl = $.Callbacks('once memory unique stopOnFalse'); fn1 = function (data) { console.log(data); }; fn2 = function (data) { console.log('fn2 say:', data); return false; }; cl.add(fn1); cl.fire('Nicholas'); // Nicholas // 由于我们使用memory参数,保存了执行环境,在添加新的函数时自动执行一次 cl.add(fn2); // fn2 say: Nicholas // 由于我们使用once参数,所以只能执行(fire)一次,此处无任何输出 cl.fire('Lee'); // 后面我们假设这里没有传入once参数,每次fire都可以执行 cl.fire('Lee'); // Lee fn2 say: Lee // 清空列表 cl.empty(); cl.add(fn2, fn1); // 由于我们设置了stopOnFalse,而fn2返回了false,则后添加的fn1不会执行 cl.fire('Nicholas'); // fn2 say: Nicholas // 上锁cl,禁用其操作,清除数据,但是我们添加了memory参数,它依然会对后续添加的执行一次 cl.lock(); // 无响应 cl.fire(); cl.add(fn2); // fn2 say: Nicholas // 禁用cl,禁止一切操作,清除数据 cl.disable();
除了上面所说的主要功能,还提供has/locked/disabled/fireWith/fired
等辅助函数。
其所有源码实现及注释为:
jQuery.Callbacks = function( options ) { options = typeof options === "string" ? // 将字符串中空格分割的子串,转换为值全为true的对象属性 createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value for non-forgettable lists memory, // Flag to know if list was already fired fired, // Flag to prevent firing locked, // Actual callback list list = [], // Queue of execution data for repeatable lists queue = [], // Index of currently firing callback (modified by add/remove as needed) firingIndex = -1, // Fire callbacks fire = function() { // Enforce single-firing locked = locked || options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes fired = firing = true; // 为quene队列中不同的[context, args]执行list回调列表,执行过程中会判断stopOnFalse中间中断 for ( ; queue.length; firingIndex = -1 ) { memory = queue.shift(); while ( ++firingIndex < list.length ) { // Run callback and check for early termination if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && options.stopOnFalse ) { // Jump to end and forget the data so .add doesn't re-fire firingIndex = list.length; memory = false; } } } // Forget the data if we're done with it if ( !options.memory ) { memory = false; } firing = false; // Clean up if we're done firing for good // 如果不再执行了,就将保存回调的list清空,对内存更好 if ( locked ) { // Keep an empty list if we have data for future add calls if ( memory ) { list = []; // Otherwise, this object is spent } else { list = ""; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // If we have memory from a past run, we should fire after adding // 如果我们选择缓存执行环境,会在新添加回调时执行一次保存的环境 if ( memory && !firing ) { firingIndex = list.length - 1; queue.push( memory ); } ( function add( args ) { jQuery.each( args, function( _, arg ) { // 如果是函数,则判断是否去重,如果为类数组,则递归执行该内部函数 if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { // Inspect recursively add( arg ); } } ); } )( arguments ); if ( memory && !firing ) { fire(); } } return this; }, // Remove a callback from the list // 移除所有的相同回调,并同步将firingIndex-1 remove: function() { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( index <= firingIndex ) { firingIndex--; } } } ); return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. // 检查是否存在该函数,如果不传递参数,则返回是否有回调函数 has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : list.length > 0; }, // Remove all callbacks from the list empty: function() { if ( list ) { list = []; } return this; }, // Disable .fire and .add // Abort any current/pending executions // Clear all callbacks and values // 置locked为[],即!![] === true,同时将队列和列表都清空,即禁用了该回调集合 disable: function() { locked = queue = []; list = memory = ""; return this; }, disabled: function() { return !list; }, // Disable .fire // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions // 不允许执行,但如果有缓存,则我们允许添加后在缓存的环境下执行新添加的回调 lock: function() { locked = queue = []; if ( !memory && !firing ) { list = memory = ""; } return this; }, locked: function() { return !!locked; }, // Call all callbacks with the given context and arguments // 为fire附带了一个上下文来调用fire函数, fireWith: function( context, args ) { if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; queue.push( args ); if ( !firing ) { fire(); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; };
jQuery.Deferred
对象
jQuery.Deferred
对象是一个工厂函数,返回一个用于异步或同步调用的deferred
对象,支持链式调用、回调函数队列,并且能针对返回的状态不同执行不同的回调。它类似于ES6
提供的Promise
对象,提供9个主要的方法:
done
ES6
는Promise
개체를 제공하지만 내장된C++
로 구현되므로 디자인을 볼 수 없습니다.jQuery
의 소스코드를 통해 디자인 아이디어를 살펴보고 둘의 차이점을 비교해 보면 어떨까요? 이 글은jquey-3.1.2.js
버전을 사용하고 있으며, 영문 댓글은 원본이고 중국어 댓글은 제가 추가한 것입니다. 🎜
🎜jQuery
의ajax
전체 디자인jQuery
는 전역ajax
매개변수를 내부적으로 설정합니다. 각ajax
요청을 초기화할 때 전달된 매개변수를 기본 전역 매개변수와 혼합하고jqXHR
객체를 빌드합니다(네이티브XHR< /code보다 더 나은 성능 제공) >자체 메소드를 구현하는 동안 더 풍부한 메소드) 전달된 매개변수를 통해 크로스 도메인인지 여부, 전달된 매개변수 유형 등을 확인하고 관련 헤더 정보를 설정합니다. 동시에 비동기 작업을 위한 내장 <code>Deferred
개체로 초기화되고(나중에 설명)done/fail
메서드가 콜백으로 추가됩니다. 동시에$.ajax
메서드를 빠르게 호출하기 위해$.get/$.post
메서드도 캡슐화했습니다. 🎜🎜위에 언급된Deferred
개체는 ES6Promise
개체와 유사하며 보다 편리한 비동기 작업, 다중 콜백 및 더 나은 작성 방법에 사용됩니다.progress/fail/done
메서드를 제공하고 개체의notify/reject/resolve
메서드로 각각 트리거합니다.then
을 사용할 수 있습니다. 세 가지 메서드를 신속하게 설정하려면always
를 사용하여 실행할 콜백을 추가하고when
메서드를 제공하여 콜백을 병합하는 여러 비동기 작업을 지원하세요. 다양한 콜백 목록을 추가할 수 있습니다. 콜백 목록은 대기열 방식으로 실행하는 데 더 편리한 내부콜백
개체를 사용합니다. 🎜🎜콜백
콜백 대기열 개체는 작업이 완료된 후 실행될 작동하기 쉬운 콜백 함수 모음을 만드는 데 사용됩니다. 네 가지 초기화 방법once/unique/memory/stopOnFalse
를 지원합니다. 이는 각각 순차 실행 전용, 중복 제거, 캐시된 결과 및 체인 호출 지원 종료를 나타냅니다. 실행, 잠금, 비활성화 여부를 나타내는fired/locked/disabled
상태 값을 제공합니다. 콜백 함수 대기열을 작동하기 위한add/remove/empty/fire/lock/disable
메서드를 제공합니다. 🎜🎜포함된 주요 컨셉은 이 3개이며, 추가 확장은 없을 예정입니다. 3개의 개체에 대한 디자인 코드의 줄 수는 약 1,200줄입니다. ('ཀ`' ∠ ). 이 세 가지 역순으로 시작하여 디자인을 분석해 보겠습니다. 🎜
🎜jQuery.Callbacks
개체콜백
개체는 콜백 함수를 관리하는 데 사용되는 다목적 목록입니다. 6가지 주요 메소드를 제공합니다: 🎜- 🎜
add
: 목록에 콜백 함수 추가 🎜 - 🎜< code> 제거: 목록에서 콜백 함수 제거🎜
- 🎜
비어 있음
: 목록에서 콜백 함수 지우기🎜 - 🎜
fire
: 목록의 콜백 함수를 순서대로 실행🎜 - 🎜
lock
: 목록을 잠그고 모든 작업을 금지하며 데이터를 지우지만 캐시된 환경 변수는 유지합니다. (memory
매개변수를 사용할 때만 유용합니다.) 🎜 - 🎜
disable
: 콜백 목록을 비활성화하고 모든 데이터를 삭제합니다🎜 ol> 🎜초기화 중에는 공백으로 구분된 4개의 매개변수가 지원됩니다. 🎜 - 🎜
한 번
: 콜백 목록은 순서대로만 실행됩니다🎜 - 🎜
memory
: 캐시 실행 환경, 새 콜백 추가 시 한 번 실행 🎜 - 🎜
unique
: 중복 제거, 각 함수가 모두 다릅니다(참조 주소 참조) 🎜 - 🎜
stopOnFalse
: 호출 도중 이전 함수가false
를 반환하면 후속 목록 실행이 중단됩니다 🎜 - 🎜
done
: 작업이 성공적으로 응답할 때의 콜백 함수(동기식 또는 비동기식, 아래와 동일) 🎜 fail
: 操作失败响应时的回调函数progress
: 操作处理过程中的回调函数resolve
: 通过该方法解析该操作为成功状态,调用donereject
: 通过该方法解析该操作为失败状态,调用failnotify
: 通过该方法解析该操作为执行过程中,调用progressthen
: 设置回调的简写,接收三个参数,分别是done/fail/progressalways
: 设置必须执行的回调,无论是done还是failpromise
: 返回一个受限制的Deferred对象,不允许外部直接改变完成状态
🎜위에서 언급한 주요 기능 외에도// 我们封装Deferred产生一个promise对象,其不能被外部手动解析,只能内部确定最终状态 asynPromise = function () { let d = $.Deferred(); (function timer() { setTimeout(function () { // 产生随机数,代替解析结果,来确定本次的状态 let num = Math.random(); if (num > 0.9) { d.resolve(); // 解析成功 } else if (num < 0.1) { d.reject(); // 解析失败 } else { d.notify(); // 解析过程中 } setTimeout(timer, 1000); // 持续不断的解析数据 }, 1000); })(); // 如果不返回promise对象,则可以被外部手动调整解析状态 return d.promise(); }; // then方法的三个参数分别代表完成、失败、过程中的回调函数 asynPromise().then(function () { console.log('resolve success'); }, function () { console.log('reject fail'); }, function () { console.log('notify progress'); }); // 本地执行结果(每个人的不一样,随机分布,但最后一个一定是success或fail) notify progress notify progress notify progress notify progress notify progress reject fail // 后面不会再有输出,因为一旦解析状态为success或fail,则不会再改变
로그인 후 복사로그인 후 복사has/locked/와 같은 보조 기능도 제공합니다. 비활성화/fireWith/해고
기능. 🎜🎜모든 소스 코드 구현 및 설명은 다음과 같습니다. 🎜Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] // 用于后面进行第一个参数绑定调用第二个参数,第三个和第四个参数分别是其不同的回调函数队列 [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { state: function() { return state; }, // 同时添加done和fail句柄 always: function() { deferred.done( arguments ).fail( arguments ); return this; }, "catch": function( fn ) { return promise.then( null, fn ); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; function resolve( depth, deferred, handler, special ) { return function() { var that = this, args = arguments, mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts if ( depth < maxDepth ) { return; } returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( jQuery.isFunction( then ) ) { // Special processors (notify) just wait for resolution if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { // ...and disregard older resolution values maxDepth++; then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions // 只有普通的process能处理异常,其余的要进行捕获,这里不是特别明白,应该是因为没有改最终的状态吧 process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object // 通过该promise对象返回一个新的扩展promise对象或自身 promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods // 给promise添加done/fail/progress事件,并添加互相的影响关系,并为deferred对象添加3个事件函数notify/resolve/reject jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state // 只有done和fail有resolved和rejected状态字段,给两个事件添加回调,禁止再次done或者fail,锁住progress不允许执行回调 if ( stateString ) { list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire // 执行第二个回调列表 list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } // 绑定notify/resolve/reject的事件,实际执行的函数体为加入上下文的With函数 deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise // 将deferred扩展为一个promise对象 promise.promise( deferred ); // Call given func if any // 在创建前执行传入的回调函数进行修改 if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; },
로그인 후 복사로그인 후 복사
🎜jQuery.Deferred
객체jQuery.Deferred
객체는 비동기식 또는 동기식으로 호출되는 사용자지연
개체는 체인 호출과 콜백 함수 대기열을 지원하고 다양한 반환 상태에 따라 다양한 콜백을 실행할 수 있습니다.ES6
에서 제공하는Promise
객체와 유사하며 9가지 주요 메소드를 제공합니다: 🎜它的实现思想是创建一个对象,包含不同状态下回调函数的队列,并在状态为失败或成功后不允许再次改变。通过返回的
Deferred
对象进行手动调用resolve/reject/notify
方法来控制流程。看一个实例(纯属胡扯,不要当真)。我们需要从间谍卫星返回的数据用不同的算法来进行解析,如果解析结果信号强度大于90%,则证明该数据有效,可以被解析;如果强度小于10%,则证明只是宇宙噪音;否则,证明数据可能有效,换一种算法解析:
// 我们封装Deferred产生一个promise对象,其不能被外部手动解析,只能内部确定最终状态 asynPromise = function () { let d = $.Deferred(); (function timer() { setTimeout(function () { // 产生随机数,代替解析结果,来确定本次的状态 let num = Math.random(); if (num > 0.9) { d.resolve(); // 解析成功 } else if (num < 0.1) { d.reject(); // 解析失败 } else { d.notify(); // 解析过程中 } setTimeout(timer, 1000); // 持续不断的解析数据 }, 1000); })(); // 如果不返回promise对象,则可以被外部手动调整解析状态 return d.promise(); }; // then方法的三个参数分别代表完成、失败、过程中的回调函数 asynPromise().then(function () { console.log('resolve success'); }, function () { console.log('reject fail'); }, function () { console.log('notify progress'); }); // 本地执行结果(每个人的不一样,随机分布,但最后一个一定是success或fail) notify progress notify progress notify progress notify progress notify progress reject fail // 后面不会再有输出,因为一旦解析状态为success或fail,则不会再改变
로그인 후 복사로그인 후 복사除了上面的主要功能,还提供了
notifyWith/resolveWith/rejectWith/state
辅助方法。其所有的源码实现和注释为:
Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] // 用于后面进行第一个参数绑定调用第二个参数,第三个和第四个参数分别是其不同的回调函数队列 [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { state: function() { return state; }, // 同时添加done和fail句柄 always: function() { deferred.done( arguments ).fail( arguments ); return this; }, "catch": function( fn ) { return promise.then( null, fn ); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; function resolve( depth, deferred, handler, special ) { return function() { var that = this, args = arguments, mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts if ( depth < maxDepth ) { return; } returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( jQuery.isFunction( then ) ) { // Special processors (notify) just wait for resolution if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { // ...and disregard older resolution values maxDepth++; then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions // 只有普通的process能处理异常,其余的要进行捕获,这里不是特别明白,应该是因为没有改最终的状态吧 process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object // 通过该promise对象返回一个新的扩展promise对象或自身 promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods // 给promise添加done/fail/progress事件,并添加互相的影响关系,并为deferred对象添加3个事件函数notify/resolve/reject jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state // 只有done和fail有resolved和rejected状态字段,给两个事件添加回调,禁止再次done或者fail,锁住progress不允许执行回调 if ( stateString ) { list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire // 执行第二个回调列表 list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } // 绑定notify/resolve/reject的事件,实际执行的函数体为加入上下文的With函数 deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise // 将deferred扩展为一个promise对象 promise.promise( deferred ); // Call given func if any // 在创建前执行传入的回调函数进行修改 if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; },
로그인 후 복사로그인 후 복사jQuery.when
方法$.when()
提供一种方法执行一个或多个函数的回调函数。如果传入一个延迟对象,则返回该对象的Promise对象,可以继续绑定其余回调,在执行结束状态之后也同时调用其when
回调函数。如果传入多个延迟对象,则返回一个新的master
延迟对象,跟踪所有的聚集状态,如果都成功解析完成,才调用其when
回调函数;如果有一个失败,则全部失败,执行错误回调。其使用方法:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")) .then(myFunc, myFailure);
로그인 후 복사其所有源码实现和注释为(能力有限,有些地方实在不能准确理解执行流程):
// 给when传递的对象绑定master.resolve和master.reject,用于聚集多异步对象的状态 function adoptValue( value, resolve, reject, noValue ) { var method; try { // Check for promise aspect first to privilege synchronous behavior // 如果when传入的参数promise方法可用,则封装promise并添加done和fail方法调用resolve和reject if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { method.call( value ).done( resolve ).fail( reject ); // Other thenables // 否则,就判断传入参数的then方法是否可用,如果可用就传入resolve和reject方法 } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { method.call( value, resolve, reject ); // Other non-thenables // 如果均不可用,则为非异步对象,直接resolve解析原值 } else { // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: // * false: [ value ].slice( 0 ) => resolve( value ) // * true: [ value ].slice( 1 ) => resolve() resolve.apply( undefined, [ value ].slice( noValue ) ); } // For Promises/A+, convert exceptions into rejections // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in // Deferred#then to conditionally suppress rejection. } catch ( value ) { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context // 一个安卓4.0的bug,这里不做阐释 reject.apply( undefined, [ value ] ); } } // Deferred helper when: function( singleValue ) { var // count of uncompleted subordinates remaining = arguments.length, // count of unprocessed arguments i = remaining, // subordinate fulfillment data resolveContexts = Array( i ), resolveValues = slice.call( arguments ), // the master Deferred master = jQuery.Deferred(), // subordinate callback factory // 将每一个响应的环境和值都保存到列表里,在全部完成后统一传给主Promise用于执行 updateFunc = function( i ) { return function( value ) { resolveContexts[ i ] = this; resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { master.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve // 如果只有一个参数,则直接将其作为master的回调 if ( remaining <= 1 ) { adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, !remaining ); // Use .then() to unwrap secondary thenables (cf. gh-3000) if ( master.state() === "pending" || jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { return master.then(); } } // Multiple arguments are aggregated like Promise.all array elements // 多参数时,进行所有参数的解析状态聚合到master上 while ( i-- ) { adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } return master.promise(); }
로그인 후 복사后续
本来想把
jQuery.Deferred
和jQuery.ajax
以及ES6
的Promise
对象给统一讲一下,结果发现牵涉的东西太多,每一个都可以单独写一篇文章,怕大家说太长不看,这里先写第一部分jQuery.Deferred
吧,后续再补充另外两篇。看
jQuery
的文档很容易,使用也很方便,但其实真正想要讲好很复杂,更不要说写篇源码分析文章了。真的是努力理解设计者的思路,争取每行都能理解边界条件,但踩坑太少,应用场景太少,确实有很大的疏漏,希望大家能够理解,不要偏听一面之词。위 내용은 Deferred 비동기 객체의 jQuery 소스 코드 파싱 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!
- 🎜

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

jQuery에서 PUT 요청 방법을 사용하는 방법은 무엇입니까? jQuery에서 PUT 요청을 보내는 방법은 다른 유형의 요청을 보내는 것과 유사하지만 몇 가지 세부 사항과 매개 변수 설정에 주의해야 합니다. PUT 요청은 일반적으로 데이터베이스의 데이터 업데이트 또는 서버의 파일 업데이트와 같은 리소스를 업데이트하는 데 사용됩니다. 다음은 jQuery에서 PUT 요청 메소드를 사용하는 구체적인 코드 예제입니다. 먼저 jQuery 라이브러리 파일을 포함했는지 확인한 다음 $.ajax({u를 통해 PUT 요청을 보낼 수 있습니다.

제목: jQuery 팁: 페이지에 있는 모든 태그의 텍스트를 빠르게 수정하세요. 웹 개발에서는 페이지의 요소를 수정하고 조작해야 하는 경우가 많습니다. jQuery를 사용할 때 페이지에 있는 모든 태그의 텍스트 내용을 한 번에 수정해야 하는 경우가 있는데, 이는 시간과 에너지를 절약할 수 있습니다. 다음은 jQuery를 사용하여 페이지의 모든 태그 텍스트를 빠르게 수정하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 먼저 jQuery 라이브러리 파일을 도입하고 다음 코드가 페이지에 도입되었는지 확인해야 합니다. <

제목: jQuery를 사용하여 모든 태그의 텍스트 내용을 수정합니다. jQuery는 DOM 작업을 처리하는 데 널리 사용되는 인기 있는 JavaScript 라이브러리입니다. 웹 개발을 하다 보면 페이지에 있는 링크 태그(태그)의 텍스트 내용을 수정해야 하는 경우가 종종 있습니다. 이 기사에서는 jQuery를 사용하여 이 목표를 달성하는 방법을 설명하고 구체적인 코드 예제를 제공합니다. 먼저 페이지에 jQuery 라이브러리를 도입해야 합니다. HTML 파일에 다음 코드를 추가합니다.

jQuery를 사용하여 요소의 높이 속성을 제거하는 방법은 무엇입니까? 프런트엔드 개발에서는 요소의 높이 속성을 조작해야 하는 경우가 종종 있습니다. 때로는 요소의 높이를 동적으로 변경해야 할 수도 있고 요소의 높이 속성을 제거해야 하는 경우도 있습니다. 이 기사에서는 jQuery를 사용하여 요소의 높이 속성을 제거하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. jQuery를 사용하여 높이 속성을 연산하기 전에 먼저 CSS의 높이 속성을 이해해야 합니다. height 속성은 요소의 높이를 설정하는 데 사용됩니다.

동시 및 비동기 프로그래밍 동시 프로그래밍은 동시에 실행되는 여러 작업을 처리하며, 비동기 프로그래밍은 작업이 스레드를 차단하지 않는 일종의 동시 프로그래밍입니다. asyncio는 프로그램이 메인 스레드를 차단하지 않고 I/O 작업을 수행할 수 있도록 하는 Python의 비동기 프로그래밍용 라이브러리입니다. 이벤트 루프 asyncio의 핵심은 I/O 이벤트를 모니터링하고 해당 작업을 예약하는 이벤트 루프입니다. 코루틴이 준비되면 이벤트 루프는 I/O 작업을 기다릴 때까지 이를 실행합니다. 그런 다음 코루틴을 일시 중지하고 다른 코루틴을 계속 실행합니다. 코루틴 코루틴은 실행을 일시 중지하고 다시 시작할 수 있는 함수입니다. asyncdef 키워드는 코루틴을 만드는 데 사용됩니다. 코루틴은 I/O 작업이 완료될 때까지 기다리기 위해 wait 키워드를 사용합니다. 다음과 같은 asyncio의 기본 사항

jQuery는 웹 페이지에서 DOM 조작 및 이벤트 처리를 처리하는 데 널리 사용되는 인기 있는 JavaScript 라이브러리입니다. jQuery에서 eq() 메서드는 지정된 인덱스 위치에서 요소를 선택하는 데 사용됩니다. 구체적인 사용 및 적용 시나리오는 다음과 같습니다. jQuery에서 eq() 메서드는 지정된 인덱스 위치에 있는 요소를 선택합니다. 인덱스 위치는 0부터 계산되기 시작합니다. 즉, 첫 번째 요소의 인덱스는 0이고 두 번째 요소의 인덱스는 1입니다. eq() 메소드의 구문은 다음과 같습니다: $("s

비동기식 및 비차단 기술을 사용하여 전통적인 예외 처리를 보완하여 보다 응답성이 뛰어나고 효율적인 Java 애플리케이션을 생성할 수 있습니다. 비동기식 예외 처리: 다른 스레드나 프로세스에서 예외를 처리하여 기본 스레드가 계속 실행되도록 하고 차단을 방지합니다. 비차단 예외 처리: I/O 작업이 잘못되었을 때 이벤트 기반 예외 처리를 포함하여 스레드 차단을 방지하고 이벤트 루프가 예외를 처리하도록 허용합니다.

jQuery 요소에 특정 속성이 있는지 어떻게 알 수 있나요? jQuery를 사용하여 DOM 요소를 조작할 때 요소에 특정 속성이 있는지 확인해야 하는 상황이 자주 발생합니다. 이 경우 jQuery에서 제공하는 메소드를 사용하여 이 기능을 쉽게 구현할 수 있습니다. 다음은 jQuery 요소에 특정 속성이 있는지 확인하기 위해 일반적으로 사용되는 두 가지 방법을 특정 코드 예제와 함께 소개합니다. 방법 1: attr() 메서드와 typeof 연산자를 // 사용하여 요소에 특정 속성이 있는지 확인
