JavaScript的Deferred是比較高大上的東西, 主要的應用還是主ajax的應用, 因為JS和nodeJS這幾年的普及, 前端的代碼越來越多, 各種回調套回調再套回調實在太讓人崩潰, 所以就從后端拖了一個延遲對象這貨, 用來解決回調地獄這個問題 。
我們使用ajax的時候多數都是為ajax添加回調 ,然后異步向服務器發送請求, 比如如下原生的XHR代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 填坑必備 </title> </head> <body> <div id="results"> data </div> <script> var results = document.getElementById('results'); var r = new XMLHttpRequest(); r.open("GET", "http://www.filltext.com?rows=10&f={firstName}", true); r.onreadystatechange = function () { if (r.readyState != 4 || r.status != 200) return; var data = JSON.parse(r.responseText); for (i=0;i<data.length;i++){ results.innerHTML += '<li>'+data[i].f+'</li>' }; }; r.send(); </script> </body> </html>
因為jQ1.5以后版本的ajax的實例對象繼承了延遲對象, 我們可以使用ajax實例的then或者done以及fail等等方法 ,所以上面的代碼可以改寫為:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 填坑必備 </title> </head>
//bootcdn這個cdn滿好用的, 上面好多庫哇, 推薦一下; <script src="http://cdn.bootcss.com/jquery/2.1.3/jquery.min.js"></script> <body> <div id="results"> data </div> <script>
//then : $.get, $.post, $.ajax返回的對象是延遲對象(deferred);
$.get("http://www.filltext.com?rows=10&f={firstName}").then(function(data){
console.log(data); for (i=0;i<data.length;i++){ $("#results").html(function(){ return $.map(data,function( obj ) { return obj.f }).join(" || ") }) }; }); </script> </body> </html>
下劃線的這個是網上抄的,不想看可以略過:異步模式在web編程中變得越來越重要,對于web主流語言Javscript來說,這種模式實現起來不是很利索,為此,許多Javascript庫(比如 jQuery和Dojo)添加了一種稱為promise的抽象(有時也稱之為deferred)。通過這些庫,開發人員能夠在實際編程中使用 promise模式。
先說下延遲對象的三種狀態, 當一個延遲對象被初始化那么該對象默認為peding狀態
1:pending等待狀態
2:fulfilled執行完成狀態
3:rejected拒絕狀態;
延遲對象的兩個主要方法:
1:add 這個方法是添加成功回調
2:fail 這個方法是添加拒絕回調
延遲對象比較高級的方法:
1:then方法; 該方法返回一個包裹了舊延遲對象的新延遲對象
2:promise方法;這個對象返回的是閹割后的Defered(延遲對象),主要作用是保護原來延遲對象;
3:when這個方法不是在延遲對象的實例上, (根據庫的不同, when這個方法在不同的地方,比如jQ的when方法是$.when,別的庫( •̀ ω •́ ) 也說不準, 反正when的源代碼是在Deferred附近), 但是和延遲對象息息相關, 因為這個方法返回的也是一個延遲對象, 顧名思義when的作用就是:when你傳的幾個延遲對象全部resolve以后, when的返回實例會resolve....懂得自然懂, 不急;
下面這張圖是jQ這個Deferred實例對象的方法, 提供參考:
以下的JS代碼是《司徒框架設計》里面介紹的延遲對象mochikit, 可以自己在控制臺跟蹤一下就知道執行的流程:
mochikit延遲對象源代碼如下, 這個延遲對象很好理解;GitHub的地址是: https://github.com/mochi/mochikit/
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> </head> <body> <script> /** @id MochiKit.Async.Deferred */ var MochiKitDeferred = function (/* optional */ canceller) { this.chain = []; this.id = this._nextId(); this.fired = -1; this.paused = 0; this.results = [null, null]; this.canceller = canceller; this.silentlyCancelled = false; this.chained = false; this.finalized = false; this.GenericError = function(msg){ return new Error("GenericError"+msg); }; this.CancelledError = function(msg) { return new Error("CancelledError"+msg); }; }; MochiKitDeferred.prototype = { /** @id MochiKit.Async.Deferred.prototype.repr */ repr: function () { return 'Deferred(' + this.id + ', ' + this.state() + ')'; }, toString: "", _nextId: function() { //return setTimeout("",0),中間要有一個空格, 要么不行; return setTimeout(" ",0); }, /** @id MochiKit.Async.Deferred.prototype.state */ state: function () { if (this.fired == -1) { return 'unfired'; } else if (this.fired === 0) { return 'success'; } else { return 'error'; } }, /** @id MochiKit.Async.Deferred.prototype.cancel */ cancel: function (e) { var self = this; if (this.fired == -1) { if (this.canceller) { this.canceller(this); } else { this.silentlyCancelled = true; } if (this.fired == -1) { if (typeof(e) === 'string') { e = new self.GenericError(e); } else if (!(e instanceof Error)) { e = new self.CancelledError(this); } this.errback(e); } } else if ((this.fired === 0) && (this.results[0] instanceof self.Deferred)) { this.results[0].cancel(e); } }, _resback: function (res) { /*** The primitive that means either callback or errback ***/ this.fired = ((res instanceof Error) ? 1 : 0); this.results[this.fired] = res; if (this.paused === 0) { this._fire(); } }, _check: function () { if (this.fired != -1) { if (!this.silentlyCancelled) { throw new MochiKit.Async.AlreadyCalledError(this); } this.silentlyCancelled = false; return; } }, /** @id MochiKit.Async.Deferred.prototype.callback */ callback: function (res) { this._check(); if (res instanceof MochiKit.Async.Deferred) { throw new Error("Deferred instances can only be chained if they are the result of a callback"); } this._resback(res); }, /** @id MochiKit.Async.Deferred.prototype.errback */ errback: function (res) { this._check(); var self = MochiKit.Async; if (res instanceof self.Deferred) { throw new Error("Deferred instances can only be chained if they are the result of a callback"); } if (!(res instanceof Error)) { res = new self.GenericError(res); } this._resback(res); }, /** @id MochiKit.Async.Deferred.prototype.addBoth */ addBoth: function (fn) { return this.addCallbacks(fn, fn); }, /** @id MochiKit.Async.Deferred.prototype.addCallback */ addCallback: function (fn) { if (arguments.length > 1) { fn = MochiKit.Base.partial.apply(null, arguments); } return this.addCallbacks(fn, null); }, /** @id MochiKit.Async.Deferred.prototype.addErrback */ addErrback: function (fn) { if (arguments.length > 1) { fn = MochiKit.Base.partial.apply(null, arguments); } return this.addCallbacks(null, fn); }, /** @id MochiKit.Async.Deferred.prototype.addCallbacks */ addCallbacks: function (cb, eb) { if (this.chained) { throw new Error("Chained Deferreds can not be re-used"); } if (this.finalized) { throw new Error("Finalized Deferreds can not be re-used"); } this.chain.push([cb, eb]); //已經觸發了, 讓他emitDirect; if (this.fired >= 0) { this._fire(); } return this; }, _fire: function () { /*** Used internally to exhaust the callback sequence when a result is available. ***/ var chain = this.chain; var fired = this.fired; var res = this.results[fired]; var self = this; var cb = null; while (chain.length > 0 && this.paused === 0) { // Array var pair = chain.shift(); var f = pair[fired]; if (f === null) { continue; }; try { res = f(res); fired = ((res instanceof Error) ? 1 : 0); if (res instanceof MochiKitDeferred) { //new如果返回的是延遲對象, 那么this的.paused就被卡住了; cb = function (res) { self.paused--; self._resback(res); }; /* */ this.paused++; }; } catch (err) { fired = 1; if (!(err instanceof Error)) { err = new MochiKitDeferred.GenericError(err); } res = err; }; }; this.fired = fired; this.results[fired] = res; if (this.chain.length == 0 && this.paused === 0 && this._finalizer) { this.finalized = true; this._finalizer(res); } if (cb && this.paused) { // this is for "tail recursion" in case the dependent deferred // is already fired res.addBoth(cb); res.chained = true; } } }; //這個延遲對象最常用方式是這樣: var df = new MochiKitDeferred(); df.addBoth(function(){ console.log(1); }).addBoth(function(){ console.log(2) }) df._resback(); // 控制他打出 ==>1 \n\r 2 \n\r;
//這個延遲對象最常用方式是這樣; //當然, 你可以把上面一個函數的返回值作為下一個函數的參數, 如下: var df = new MochiKitDeferred(); df.addBoth(function(){ return 0 }).addBoth(function(arg){ console.log(arg); return 1; }).addBoth(function(arg){ console.log(arg); return 2; }).addBoth(function(arg){ console.log(arg); }) df._resback(); // 控制他打出 ==>1 \n\r 2 \n\r; </script> </body> </html>
博客園上博友教你寫一個比較簡單的延遲對象, 地址是: http://www.cnblogs.com/ygm125/p/3735677.html
是代碼量比較少的, 也好理解, 實在不懂就開控制器一步一步跟蹤, 多看幾遍, 不懂都說不過去:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> </head> <body> <script> (function(window,undefined){ var PENDING = undefined, FULFILLED = 1, REJECTED = 2; var isFunction = function(obj){ return 'function' === typeof obj; } var isArray = function(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; } var isThenable = function(obj){ return obj && typeof obj['then'] == 'function'; } var transition = function(status,value){ var promise = this; if(promise._status !== PENDING) return; // 所以的執行都是異步調用,保證then是先執行的 setTimeout(function(){ promise._status = status; publish.call(promise,value); }); } var publish = function(val){ var promise = this, fn, st = promise._status === FULFILLED, queue = promise[st ? '_resolves' : '_rejects']; while(fn = queue.shift()) { val = fn.call(promise, val) || val; } promise[st ? '_value' : '_reason'] = val; promise['_resolves'] = promise['_rejects'] = undefined; } var Promise = function(resolver){ if (!isFunction(resolver)) throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); if(!(this instanceof Promise)) return new Promise(resolver); var promise = this; promise._value; promise._reason; promise._status = PENDING; promise._resolves = []; promise._rejects = []; var resolve = function(value){ transition.apply(promise,[FULFILLED].concat([value])); } var reject = function(reason){ transition.apply(promise,[REJECTED].concat([reason])); } resolver(resolve,reject); } Promise.prototype.then = function(onFulfilled,onRejected){ var promise = this; // 每次返回一個promise,保證是可thenable的 return Promise(function(resolve,reject){ function callback(value){ var ret = isFunction(onFulfilled) && onFulfilled(value) || value; if(isThenable(ret)){ ret.then(function(value){ resolve(value); },function(reason){ reject(reason); }); }else{ resolve(ret); } } function errback(reason){ reason = isFunction(onRejected) && onRejected(reason) || reason; reject(reason); } if(promise._status === PENDING){ promise._resolves.push(callback); promise._rejects.push(errback); }else if(promise._status === FULFILLED){ // 狀態改變后的then操作,立刻執行 callback(promise._value); }else if(promise._status === REJECTED){ errback(promise._reason); } }); } Promise.prototype.catch = function(onRejected){ return this.then(undefined, onRejected) } Promise.prototype.delay = function(ms){ return this.then(function(val){ return Promise.delay(ms,val); }) } Promise.delay = function(ms,val){ return Promise(function(resolve,reject){ setTimeout(function(){ resolve(val); },ms); }) } Promise.resolve = function(arg){ return Promise(function(resolve,reject){ resolve(arg) }) } Promise.reject = function(arg){ return Promise(function(resolve,reject){ reject(arg) }) } Promise.all = function(promises){ if (!isArray(promises)) { throw new TypeError('You must pass an array to all.'); } return Promise(function(resolve,reject){ var i = 0, result = [], len = promises.length; function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ result[index] = value; if(index == len - 1){ resolve(result); } } for (; i < len; i++) { promises[i].then(resolver(i),rejecter); } }); } Promise.race = function(promises){ if (!isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return Promise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); } window.Promise = Promise; })(window); //常見的使用方式如下: var getData100 = function(){ return Promise(function(resolve,reject){ setTimeout(function(){ resolve('100ms'); },100); }); } var getData200 = function(){ return Promise(function(resolve,reject){ setTimeout(function(){ resolve('200ms'); },200); }); } getData100().then(function(data){ console.log(data); // 100ms return getData200(); }).then(function(data){ console.log(data); // 200ms return data + data; }).then(function(data){ console.log(data) // 200ms200ms }); </script> </body> </html>
公司這邊的老段寫的Deferred, 是從TypeScript編譯成js的, 因為這個東西比較簡單, 應該不算泄露公司機密哇, 代碼的最后有幾個使用的實例, 你點擊運行即可查看, 有興趣自己寫一個延遲對象, 印象會更加深刻:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> </head> <body> <script> //d為目標對象, b為一個函數對象; var __extends = this.__extends || function (d, b) { //繼承了靜態屬性 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } //繼承了原型 __.prototype = b.prototype; d.prototype = new __(); }; //Promise這個構造器, Deferred繼承Promise; var Promise = (function () { function Promise() { } /** * onDone/onFail 應該返回值(或拋出異常),即不應返回 undefined,忘記返回值通常是 Bug,因此會在控制臺給出警告。 * 如果確實不需要返回值,可返回 null。 */ Promise.prototype.then = function (onDone, onFail) { return null; }; Object.defineProperty(Promise.prototype, "status", { get: function () { return 0; }, enumerable: true, configurable: true }); Object.defineProperty(Promise.prototype, "result", { get: function () { return undefined; }, enumerable: true, configurable: true }); Promise.prototype.done = function (onDone) { return this; }; Promise.prototype.fail = function (onFail) { return this; }; Promise.prototype.progress = function (onProgress) { return this; }; Promise.when = function (promises) { var allDone = new Deferred(); if (!promises.length) { allDone.resolve([]); return allDone; } var resolved = 0; for (var i = 0; i < promises.length; i++) { promises[i].done(function (v) { ++resolved; if (resolved === promises.length && allDone.status === Promise.UNFULFILLED) { var results = promises.map(function (p) { return p.result; }); allDone.resolve(results); } }).fail(function (e) { if (allDone.status === Promise.UNFULFILLED) allDone.reject(e); //TODO 此處i是無用的,怎么指示是哪一個promise的信息? }).progress(function (v) { if (allDone.status === Promise.UNFULFILLED) { allDone.notify(v); //TODO 此處i是無用的,怎么指示是哪一個promise的信息? } }); } return allDone; }; Promise.UNFULFILLED = 0; Promise.RESOLVED = 1; Promise.REJECTED = 2; return Promise; })(); var Deferred = (function (_super) { //繼承原型 __extends(Deferred, _super); function Deferred() { //繼承 _super.call(this); //成功的列表; this._onDones = null; //失敗的列表; this._onFails = null; //進度的回調列表; this._onProgresses = null; // 0 : 為解決, 1 : 已解決, 2 : 被拒絕了; this._status = Promise.UNFULFILLED; this._result = undefined; if (Deferred._DEBUG) { try { throw new Error('Deferred constructor calling stack'); } catch (e) { this._stack = e; }; }; }; //直接通過訪問_status也行; Object.defineProperty(Deferred.prototype, "status", { get: function () { return this._status; }, enumerable: true, configurable: true }); //直接訪問實例的_result也行; Object.defineProperty(Deferred.prototype, "result", { get: function () { return this._result; }, enumerable: true, configurable: true }); //把callback的成功列表全部壓棧; Deferred.prototype.done = function (onDone) { if (this._status == Promise.UNFULFILLED) { this._onDones = this._onDones || []; this._onDones.push(onDone); //如果已經成功直接觸發成功回調, (這里要注意有種情況是“已經失敗”的姿態, 這個成功回調并不會入棧或者觸發); } else if (this._status == Promise.RESOLVED) this._emitEventDirectly(onDone); return this; }; //把callback的失敗列表全部壓棧; Deferred.prototype.fail = function (onFail) { if (this._status == Promise.UNFULFILLED) { this._onFails = this._onFails || []; this._onFails.push(onFail); //如果已經失敗直接觸發失敗回調; } else if (this._status == Promise.REJECTED) this._emitEventDirectly(onFail); return this; }; Deferred.prototype.progress = function (onProgress) { if (this._status == Promise.UNFULFILLED) { this._onProgresses = this._onProgresses || []; this._onProgresses.push(onProgress); } return this; }; //then這個很重要, 他會重新返回包裝后的延遲對象, 也是延遲對象里面比較復雜的東西; //功過then可以實現鏈式調用, 實例如下: /* var df = new Deferred(); df.then(function() { return 1 }).then(function(arg) { console.log(arg); return 2; }).then(function(arg){ console.log(arg) }); df.resolve(); */ Deferred.prototype.then = function (onDone, onFail) { var _this = this; var def = new Deferred(); var result; //then返回的是新的延遲對象 //done是為當前這個延遲對象添加延遲對象; this.done(function (data) { // 這個done很重要, 有三個判斷; // 如果有沒有onDone就直接為當前的resolve; 一個舊的derffer被resolve了那么新的deferred也resovle, resolve的參數為舊deferred的參數:實例如下 /* var df = new Deferred(); df.then().done(function(arg){ console.log(arg); }); df.resolve("1"); */ // 如果有onDone, onDone的返回是非Deferred對象,通過resolve(返回值)閉包內的Deferred對象, 實例如下: /* var df = new Deferred(); df.then(function() {return 2}).done(function(arg){ console.log(arg); }); df.resolve(); */ // 如果有onDone, 而且onDone返回對象是Promise的實例, 那么為返回的這個promise添加一個onDone, 這個onDone添加閉包內部的promise對象的resolve, 實例如下: /* var df = new Deferred(); df.then(function() { var closureDf = new Deferred(); setTimeout(function(){ closureDf.resolve("haha") },4000); return closureDf.promise(); }).done(function(arg){ console.log(arg); }); df.resolve(); */ if (onDone) { try { result = onDone(data); //主要是起到提示的作用; _this._warnReturnValue(result); if (result instanceof Promise) { //result又是一個延遲對象的話, 當result發生resolve的時候,def也reslove //result.done(def.resolve.bind(this)).fail(def.reject.bind(this)).progress(def.notify.bind(this)); //result.done(function(arg){ def.resolve(arg) }).fail(function(arg){ def.reject(arg) }) def._bindTo(result); // return result; } else //最好不要延遲對象套延遲對象, 會暈; def.resolve(result); } catch (err) { def.reject(err); } } else def.resolve(data); }); this.fail(function (err) { if (onFail) { try { result = onFail(err); _this._warnReturnValue(result); if (result instanceof Promise) { def._bindTo(result); return result; } else { def.resolve(result); } } catch (err2) { def.reject(err2); } } else def.reject(err); }); return def; }; Deferred.prototype.resolve = function (data) { if (typeof data === 'undefined') console.warn('>>>> Deferred.resolve() received undefined, likely a bug'); return this._emitEvent(data, Promise.RESOLVED); }; Deferred.prototype.reject = function (err) { if (Deferred._DEBUG) { try { throw new Error('Deferred.reject calling stack'); } catch (e) { logw('rejected: Defered.constructor stack:\n' + (this._stack['stack'] || this._stack) + '\nrejected: Defered.rejected stack:\n' + (e['stack'] || e) + '\nrejected: reason stack:\n' + (err['stack'] || err)); } } return this._emitEvent(err, Promise.REJECTED); }; Deferred.prototype.notify = function (data) { return this._emitEvent(data); }; //這個是觸發事件的裝飾者, 你要觸發失敗回調, 成功回調, 進度回調都需要走這邊, 只是穿進來的姿態不一樣而已; Deferred.prototype._emitEvent = function (data, status) { if (this._status != Promise.UNFULFILLED) { throw Error('fulfilled'); } var callbacks; //處理事件列表; if (status === Promise.RESOLVED) callbacks = this._onDones; else if (status === Promise.REJECTED) callbacks = this._onFails; else callbacks = this._onProgresses; //沒有status是onProgress的情況; if (status) { this._status = status; this._result = data; this._onDones = this._onFails = this._onProgresses = null; }; if (callbacks) { for (var i = 0; i < callbacks.length; i++) { try { //這個是執行回調列表, 是線性的; callbacks[i](data); } catch (e) { this._log(e); } } } return this; }; Deferred.prototype._bindTo = function (p) { p.done(this.resolve.bind(this)).fail(this.reject.bind(this)).progress(this.notify.bind(this)); }; Deferred.prototype._emitEventDirectly = function (callback) { var _this = this; if (!callback) return; setTimeout(function () { try { callback(_this._result); } catch (e) { _this._log(e); } }, 0); }; Deferred.prototype._log = function (err) { console.warn(err.stack || err); }; Deferred.prototype._warnReturnValue = function (value) { if (typeof value === 'undefined') console.warn('>>>> Promise.then(): onDone/onFail returns undefined, likely a bug'); else if (value && !(value instanceof Promise) && typeof value.then === 'function') console.warn('>>>> Promise.then(): onDone/onFail returns a promise-like object, likely a bug. Consider Promise.wrap().'); }; Deferred._DEBUG = false; return Deferred; })(Promise); //使用方法: var df = new Deferred(); df.then(function() { return 1 }).then(function(arg) { console.log(arg); return 2; }).then(function(arg){ console.log(arg) }); df.resolve(); //單純的Deferred相對于一個callbacks, 使用then方法就變成了鏈式調用(個人理解); var df = new Deferred(); df.then().done(function(arg){ console.log(arg); }); df.resolve("1"); //歡迎拍磚; var df = new Deferred(); df.then(function() {return 2}).done(function(arg){ console.log(arg); }); df.resolve(); </script> </body> </html>
jQ1.7的延遲對象, 代碼量比較少, 依賴callbacks,不太好懂, 慢慢看,參考jQ的延遲對象分析: http://www.cnblogs.com/snandy/archive/2012/12/19/2812935.html
要先弄懂callbacks這個方法, 才能在弄懂Deferred, when也只是一個Deferred的包裝。 建議先看1.5版本的Deferred, 弄懂了再看1.7的Deferred, 下面的Deferred被我單獨裁出來了,可以單獨運行, 里面的注釋是博客園的好像是aaron寫的代碼解析, 輔助大家看一下:
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <script> //jQ部分的延遲對象截取; jQuery = $ = {}; jQuery.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; var optionsCache = {}; var core_rspace = /\s+/; var core_toString = Object.prototype.toString; var class2type = {}; jQuery.each = function( obj, callback, args ) { var name, i = 0, length = obj.length, isObj = length === undefined || jQuery.isFunction( obj ); if ( args ) { if ( isObj ) { for ( name in obj ) { if ( callback.apply( obj[ name ], args ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.apply( obj[ i++ ], args ) === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isObj ) { for ( name in obj ) { if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { break; } } } }; return obj; }; $.isFunction = function( obj ) { return jQuery.type(obj) === "function"; } $.type = function( obj ) { return obj == null ? String( obj ) : class2type[ core_toString.call(obj) ] || "object"; }; jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.split( core_rspace ), function( _, flag ) { object[ flag ] = true; }); return object; }; //$.Callbacks( 'once memory unique stopOnFalse' ); jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { //如果有memory我們會把傳進來的參數保存; memory = options.memory && data; //觸發的標志; fired = true; //如果有memory的callback對象執行過了, 會有firingStart; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; //callback已經觸發過后的標志; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { //return false的時候就不走了 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } }; firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, //通過閉包, 保存局部變量, 返回self; // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; // 如果傳進來的是[fn, fn1, fn2 , [fn3, fn4, fn5, fn6]]會把數組扁平化哦 // Array.prototype.concat.apply([],[1,2,3,[4,5]]); 你懂得....; (function add( args ) { jQuery.each( args, function( _, arg ) { // if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) { list.push( arg ); } else if ( arg && arg.length ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? // 對定制選項的額外處理; if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { //沒list就不玩了; if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes // 對定制選項的額外處理; if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Control if a given callback is in the list has: function( fn ) { return jQuery.inArray( fn, list ) > -1; }, // Remove all callbacks from the list empty: function() { list = []; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( list && ( !fired || stack ) ) { if ( firing ) { stack.push( args ); } else { fire( args ); } } 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; }; //接著, 來參考一下jQ的延遲對象 , 原文地址在:http://www.cnblogs.com/lovesueee/archive/2012/10/18/2730287.html; jQuery.extend({ Deferred: function( func ) { // 數據元組集 // 每個元組分別包含一些與當前deferred相關的信息: // 分別是:觸發回調函數列表執行(函數名),添加回調函數(函數名),回調函數列表(jQuery.Callbacks對象),deferred最終狀態(第三組數據除外) // 總體而言,三個元組會有對應的三個callbacklist對應于doneList, failList, processList // 對于jQuery.Callbacks對象,可以看之前的文章http://www.cnblogs.com/lovesueee/archive/2012/10/18/2729829.html var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], // deferred的狀態,分為三種:pending(初始狀態), resolved(解決狀態), rejected(拒絕狀態) state = "pending", // promise對象,主要有兩點作用: // 1. 在初始化deferred對象時,promise對象里的方法都會被extend到deferred中去 // 2. 那么,生成的deferred對象里必然引用了promise對象的promise方法,所以當調用deferred.promise()時, // deferred對象會通過閉包返回promise對象,這就是所謂的受限制的deferred對象(用deferred2表示),因為相比之前, // 返回的deferred2不在擁有resolve(With), reject(With), notify(With)這些能改變deferred對象狀態并且執行callbacklist的方法了 promise = { // 返回閉包里的內部state(外部只讀) state: function() { return state; }, // 同時在doneList和failList的list里添加回調函數(引用) // 那么不論deferred最終狀態是resolved還是rejected, 回調函數都會被執行,這就是所謂的always always: function() { deferred.done( arguments ).fail( arguments ); return this; }, // jQuery.then()會創建一個新的受限制的deferred對象 // 有點復雜,下面我有畫一個圖幫忙理解 then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; // 創建新的受限制的deferred對象(稱作newDeferrred),并返回 // 利用返回的deferred對象就可以做很多事了,你懂的 return jQuery.Deferred(function( newDefer ) { /* var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ] */ jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], fn = fns[ i ]; // >>>> 很重要的一點就是:then這個方法是新建一個deferred,然后把當前deferred的done, fail progress三個依次添加新deferred, 并把這個deferred返回; <<<< // deferred[ done | fail | progress ] for forwarding actions to newDefer // 分別為deferred的三個callbacklist添加回調函數,根據fn的是否是函數,分為兩種情況: // 1.不是函數的情況(如值為undefined或者null等),直接鏈接到newDeferred的resolve(reject,notify)方法,也就是說 // newDeferrred的執行依賴外層的調用者deferred的狀態或者說是執行動作(resolve還是reject或者是notify) // 此時deferred.then()相當于將自己的callbacklist和newDeferred的callbacklist連接起來了,故可以在newDeferred // 中大做文章 // 2.是函數的情況,根據返回值(稱作returnReferred)是否是deferred對象,又可以分為兩種情況: // 2.1 返回值是deferred對象,那么在returnReferred對象的三個回調函數列表中添加newDeferred的resolve(reject,notify)方法 // 也就是說newDeferrred的執行依賴returnDeferred的狀態 // 2.2 返回值不是deferred對象,那么將返回值returned作為newDeferred的參數并將從外層deferred那邊的上下文環境作為newDeferred // 的執行上下文,然后執行對應的回調函數列表,此時newDeferrred的執行依賴外層的調用者deferred的狀態 //deferred.done(fn), deferred.fail(fn), deferred.progress(fn); deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? //傳進來的是函數的情況下, 函數可能不返回, 也可能返回一個延遲對象;; function() { //這行傳進來的參數, 利用閉包, fn是這個閉包的變量; var returned = fn.apply( this, arguments ); //又返回了一個延遲對象的話, 我們再把這個新的延遲對象的resolve和reject和notify放到這個deferred; if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { //這個函數返回的不是延遲對象, 把這個fn的返回值作為參數觸發newDefer; newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); } } : newDefer[ action ] ); }); fns = null; }).promise(); }, promise: function( obj ) { return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise; } }, // 實際返回的deferred對象 deferred = {}; // Keep pipe for back-compat // pipe和then引用同一個函數,所以功能是一樣的 // 只不過通常的用法是:會用pipe進行filter操作 promise.pipe = promise.then; // Add list-specific methods // 通過上面定義的數據元組集來擴展一些方法 jQuery.each( tuples, function( i, tuple ) { //就是callback了; var list = tuple[ 2 ], //resolved, rejected stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add // 給上面的promise對象添加done,fail,process方法 // 這三個方法分別引用三個不同jQuery.Callbacks對象的add方法(不是同一個引用), // 那么這三個方法的用途就是向各自的回調函數列表list(各自閉包中)中添加回調函數,互不干擾 promise[ tuple[1] ] = list.add; // Handle state // 通過stateString有值這個條件,預先向doneList,failList中的list添加三個回調函數 // doneList : [changeState, failList.disable, processList.lock] // failList : [changeState, doneList.disable, processList.lock] // changeState 指的是下面首先添加的一個改變deferred對象的匿名函數 // 可以看的出: 不論deferred對象最終是resolve(還是reject),在首先改變對象狀態之后,都會disable另一個函數列表failList(或者doneList) // 然后lock processList保持其狀態,最后執行剩下的之前done(或者fail)進來的回調函數 // 當然了,上述情況processList除外 if ( stateString ) { // 一旦觸發就會把這個閉包的姿態字符串保存的state里面去; list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] = list.fire // 給deferred對象添加resolve(With), reject(With), notify(With)方法 // 這三個方法分別引用三個不同jQuery.Callbacks對象的fire方法; deferred[ tuple[0] ] = list.fire; deferred[ tuple[0] + "With" ] = list.fireWith; }); // jQuery.extend( deferred, promise ); // 將上面的promise對象extend進deferred中 promise.promise( deferred ); // Call given func if any // 如果調用jQuery.Deferred(func)指定了參數,那么調用func并設置func的上下文和參數均為deferred // 在jQuery.then()中有用到這一點 if ( func ) { func.call( deferred, deferred ); } // All done! // 返回最終的deferred對象 return deferred; }, //suborinate:部屬;部下,下級的意思, when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, // 首先將arguments偽數組轉換為真正的數組 resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates // jQuery.isFunction( subordinate.promise )用來判斷subordinate是否是deferred對象 // 1. 在參數個數等于1的情況下: // 1.1 如果參數是deferred對象,那么remaining = length, 這是remaining就是1嘛 // 1.2 否則remaining為0 // 2. 在參數不等于1(即等于0或者大于1)的情況:remaining = length remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. // 到這里就可以知道:如果參數個數僅為1個,并且是deferred對象,那么就無需再生成deferred對象 deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { // 這里返回一個函數作為一個callback完全是為了創建一個閉包,主要是為了保持i的值 return function( value ) { // 保存各個deferred執行的上下文,也就是說之后whenDeferred的回調函數的上下文就是一個數組 contexts[ i ] = this; // 保存各個deferred執行時的參數,之后傳遞給whenDeferred的回調函數 // 此時values的值有原先的jQuery.when()傳進來的參數變為各個deferred執行回調時的參數了,也就是說覆蓋了 values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); //所有的defer都執行了以后remaining就等于0了; } else if ( !( --remaining ) ) { // 時機成熟,即所有延遲都resolve,執行whenDeferred的回調函數 deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved // 如果參數個數大于1,那么就是說有可能存在多個deferred對象 // 這時需要一些條件判斷以保證是所有的deferred對象都resolve了,再執行whenDeferred的resolve // 或者當有一個deferred對象reject了,whenDeferred的reject if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { // 如果是deferred對象, 為每一個promise添加update,失敗就執行返回deferred的fail列表; if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { // 給每個參數(deferred對象)添加最后的回調,用來檢查此時的狀態 resolveValues[ i ].promise() // 用于當每一個deferred對象resolve回來,用updateFunc返回的函數檢查此時其他deferred對象的狀態(即此時remaining是否等于0了) //updateFunc是一個閉包, 他把i傳進去了, 只有then().done()或者then().promise()有返回值, promise(),或者是done是沒有返回值的; .done( updateFunc( i, resolveContexts, resolveValues ) ) // 如果有一個deferred對象reject,whenDeferred將執行reject .fail( deferred.reject ) //updateFunc又是一個閉包, ; .progress( updateFunc( i, progressContexts, progressValues ) ); // 如果不是deferred對象,直接--remaining,視為resolve } else { --remaining; }; }; }; // if we're not waiting on anything, resolve the master // 如果此時remaining就等與0了,表示沒有什么延遲需要等待,那么立即之行whenDeferred的resolveWith // 此時resolveContexts為undefined, 這就意味這上下文將為全局的window if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } // 返回promise對象; return deferred.promise(); } }); var df = $.Deferred(); df.done(function(arg){ console.log(arg); console.log(1) }) df.done(function(arg){ console.log(arg); console.log(2) }); df.resolve("Deffffffffffrrrrred"); //延遲對象pipe的使用, pipe和then是一樣的, 指向同一個函數; var df = $.Deferred(); df.pipe(function() { var closureDf = $.Deferred(); setTimeout(function(){ closureDf.resolve("haha") },4000); console.log(closureDf); return closureDf; }).done(function(arg){alert(1) console.log(arg); }); df.resolve(); </script> </body> </html>
ES6原生的好像支持PROMISE么么噠,O(∩_∩)O哈哈~;
參考鏈接:
用法:
阮一峰的jQuery.Deferred對象: http://javascript.ruanyifeng.com/jquery/deferred.html
Aaron:
Aaron深入分析延遲對象:http://www.cnblogs.com/aaronjs/p/3356505.html
Aaron 分析 Deferred概念 :http://www.cnblogs.com/aaronjs/p/3348569.html
github:
when的主頁: https://github.com/cujojs/when/
延遲對象cho45: https://github.com/cho45/jsdeferred
學習實例:
whenJS的使用方法,來自蛐蛐的個人博客:https://www.imququ.com/post/promises-when-js.html
教你一步一步實現一個Promise:http://www.cnblogs.com/ygm125/p/3735677.html
百科:
promise規范A+: https://promisesaplus.com/
維基百科的promise/A規范說明:http://wiki.commonjs.org/wiki/Promises/A ,打開非常慢
維基百科的promise/B規范說明:http://wiki.commonjs.org/wiki/Promises/B ,打開也非常慢
文章列表