先直接上源碼吧。es6
if(!window.Promise) { function Promise(fn) { var self=this; this.status = 'pending'; this.thenCache = []; this.count = 0 if(!(this instanceof Promise)) { throw 'Defer is a constructor and should be called width "new" keyword'; } if(typeof fn !== 'function') { throw 'Defer params must be a function'; } //爲了讓傳進來的函數在then後執行 setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0); } Promise.prototype.resolve = function(value) { this.value = value; this.status = 'resolved'; this.triggerThen(); } Promise.prototype.reject = function(reason) { this.value = reason; this.status = 'rejected'; this.triggerThen(); } Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); console.log('this', this) return this; } Promise.prototype.catch = function(fn) { if(typeof fn === 'function') { this.errorHandle = fn; } }; Promise.prototype.triggerThen = function() { console.log('this.thenCache', this.thenCache) console.log('this.status', this.status) var current = this.thenCache.shift(), res; console.log('current', current) if(!current && this.status === 'resolved') { // console.log('--11--', + new Date()) return this; } else if(!current && this.status === 'rejected') { if(this.errorHandle) { this.value = this.errorHandle.call(undefined, this.value); this.status = 'resolved'; console.log('--11--', + new Date()) } return this; }; if(this.status === 'resolved') { // console.log('--222--', + new Date()) res = current.onResolve; } else if(this.status === 'rejected') { console.log('--222--', + new Date()) res = current.onReject; } this.count ++; if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = 'resolved'; console.log('-ddd--', + new Date()) this.triggerThen(); // console.log('this.count', this.count, + new Date()) } catch(e) { this.status = 'rejected'; this.value = e; return this.triggerThen(); } } else { console.log('--44--') this.triggerThen(); } } window.Promise = Promise; }
以前寫過如何構造一個promise庫,參考的美團點評的網站,寫到後面發現Promise.resolve()直接調用就會出現問題。報錯上面的庫,也是引用Talking Coder的一篇博客,逐漸開始理解promise內部是如何實現的了,固然仍是有一些疑問。好比上面的庫,須要註釋window.Promise = Promise和if(!window.Promise) {} 內部的代碼在debug的時候才能看得見。但若是註釋掉,直接Promise.resolve()又回一樣報resolve不是一個方法。promise
阮一峯在promise基礎篇提到過,阮一峯promise基礎介紹,then返回的是一個新的Promise實例,不是原來的那個實例。這裏提到的原來的那個實例,我想應該是第一次實例化的Promise實例對象。這裏有點不懂,爲什麼then函數在return this後,就會返回一個新的Promise實例對象。瀏覽器
大概講解下此Promise庫的原理吧。函數
setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0);
call 和 bind 都是爲了綁定this的指向,由於直接回調,this在瀏覽器裏面指向的是window對象,綁定fn執行的時候this指向Promise的實例對象。同理綁定resolve和reject的this指向。
setTimout 0秒後,是爲了讓fn在隊列的最後執行。這裏的隊列一會再剖析。或者說讓resolve或reject在隊列的最後執行。
若是去掉setTimeout 0秒後,那麼在實例化Promise的時候,就會馬上執行回調fn,進而執行resolve或reject函數,而此時then還將來得及push須要thenAble的回調隊列,致使再執行resolve或reject裏面的triggerThen()方法時,沒法執行(此時回調隊列爲空。)網站
Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); return this; }
then 實際是一個回調隊列,當有3個then,那麼回調隊列就會有三個。而後通this.triggerThen()
以此遞歸調用,直接回調隊列被調用完畢後,再執行fn中的resolve或reject函數。this
再分析下狀態和返回值
在triggerThen函數裏有這麼幾行代碼prototype
if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = 'resolved'; this.triggerThen(); } catch(e) { this.status = 'rejected'; this.value = e; return this.triggerThen(); } } else { this.triggerThen(); }
能夠看出,當js無語法報錯的時候,執行的try代碼。此時將then()中第一個回調函數執行的value賦值給了this.value。在new Promise()的回調函數中,若是執行過resolve()的話,那麼此時賦值就是第二次賦值了。同理this.status。當出現語法報錯的時候,會執行catch,此時將錯誤參數e一樣賦值給this.value。狀態也被變爲rejected。同理若是在new Promise()回調中執行過reject,那麼此時賦值也是第二次賦值了。這就驗證了阮一峯在Promise基礎介紹將的下面的代碼邏輯。debug
const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
catch 跟then的第二個參數是一個邏輯。並且then的第二個參數實際咱們不多回調。都寫catch來異常捕獲的。code
其實寫這篇文章的時候,對Promise仍是瞭解在表面上,但收穫依然仍是有的,準建理解了Promise是如何實現的,好比resolve和then回調的關係,對象