長篇預警!有點長,能夠選擇性觀看。若是對Promise源碼不是很清楚,仍是推薦從頭看,相信你認真從頭看到尾,而且去實際操做了,確定會有收穫的。主要是代碼部分有點多,不過好多都是重複的,沒必要擔憂git
Promise的一些用法在此很少贅述,本篇主要帶領你手寫一個Promise源碼,學完你就會發現:Promise沒有你想象中的那麼難github
實現簡單的同步Promise
先大概說一下基本概念:
Promise內部維護着三種狀態,即pending,resolved和rejected。初始狀態是pending,狀態能夠有pending--->relolved,或者pending--->rejected.不能從resolve轉換爲rejected 或者從rejected轉換成resolved.
即 只要Promise由pending狀態轉換爲其餘狀態後,狀態就不可變動。
ok.知道了這些後,咱們開始手擼代碼:npm
注意觀看序號 1 2 3 4 5 ...數組
function Promise(executor){ let that = this; /** 2 定義初始的一些變量 */ that.status = 'pending'; that.value = null; that.reason = null; /** 3 定義初始的成功和失敗函數 */ function resolve(value){ /** 4 判斷狀態是否是初始狀態pending * 是就轉換狀態 不然不轉換 * 確保狀態的變化後的不可變性 */ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; } } function reject(reason){ /** 5 同上 */ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; } } /** * 1 Promise中首先傳了一個executor,它是一個函數 * executor函數中又傳了兩個函數,分別是resolve和reject * 很顯然 resolve是成功回調,reject是失敗的回調 */ executor(resolve,reject); } /** 6 在Promise原型上面定義then方法 * then方法上面有兩個回調 一個是成功後的方法 另外一個是失敗後的方法 * 根據成功或失敗的狀態去執行相關成功onFilfulled()或者失敗onRejected()的回調方法 */ Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; if(that.status === 'resolved'){ /** 7 若是狀態已經變動爲resolved * 說明resolve方法已經被調用 * 那麼此時就執行成功的回調函數onFilfulled * 並傳入參數 that.value * */ onFilfulled(that.value); } if(that.status === 'rejected'){ /** 8 同上 * 傳入參數 that.reason */ onRejected(that.reason); } } module.exports = Promise;
經過require()引入手擼的Promisepromise
let Promise = require('./myPromise'); let p1 = ()=>{ return new Promise((resolve,reject)=>{ resolve('success.1'); }); } p1().then((data)=>{ console.log(data); // 打印 success.1 },(err)=>{ console.log(err); });
ok.經調用發現 此代碼能夠實現部分Promise的功能,但僅僅是同步下才有效果。
那異步呢? 別急這就來~:異步
增長異步功能
注意觀看序號 1 2 3 4 5 ...函數
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; /** 1 由於異步不是當即執行 狀態不會變動 成功或失敗的回調函數也不會執行 * 因此先定義好存放成功或失敗回調函數的數組 * 以便將成功或失敗的回調函數先保存起來 * */ that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; /** 3 發佈 * 等待狀態發生變動 * 狀態變動後 當即執行以前存放在相應數組中全部的成功或失敗的回調函數 * 即 發佈 */ that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; /** 4 同上 */ that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; if(that.status === 'resolved'){ onFilfulled(that.value); } if(that.status === 'rejected'){ onRejected(that.reason); } /** 2 訂閱 * 由於是異步 狀態當時並無當即變動 因此狀態仍是pending * 此時須要把成功或者失敗的回調函數存放到對應的數組中 * 等待狀態變動時 再從數組中拿出來去執行 * 即 訂閱 * *存放數組時 爲了執行時方便 須要把回調函數的外層包裹一層空函數 */ if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ onFilfulled(that.value); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ onRejected(that.reason); }); } } module.exports = Promise;
代碼測試:測試
let Promise = require('./myPromise'); let p1 = ()=>{ return new Promise((resolve,reject)=>{ setTimeout(function(){ resolve('success.1'); // reject('fail.'); },1500); }); } p1().then((data)=>{ console.log(data); // success.1 },(err)=>{ console.log(err); });
能夠看到 1.5s後 執行了resolve() 並打印了success.1,至此,咱們實現了異步的Promise.其實這裏的實現異步的思想就是發佈訂閱.ui
en~ok.高能預警🐯.接下來就稍稍複雜了 由於咱們要實現鏈式調用then。 要實現這個功能那咱們就要重寫then方法,並在then方法中從新返回一個Promise,只有這樣,才能夠實現屢次調用then.並且要新增一個解析返回值是否爲promise的函數.this
稍微捋下邏輯:
👌 上代碼:
增長鏈式調用then
注意觀看序號 1 2 3 4 5 ...
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; /** 1 讓promise2等於一個新的Promise 並將promise2返回 */ let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ /** 2 由於返回了promise2 * 而且第3步resolvePromiseRelation函數中傳遞了promise2 * 而目前promise2並無拿到 * 因此加一個定時器 異步執行 等到promise2拿到後 * 再去執行 resolvePromiseRelation()方法 並將promise2傳遞進去*/ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); /** 3 判斷新返回值是什麼類型的函數 * 並將當前的promise:promise2 新的返回值:promise3 * 和 成功時回調:esolve 失敗時回調:reject 做爲參數傳進去 */ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); /** 同1 */ return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ /** 4 防止本身等待本身 一直循環等待 */ if(promise2 === promise3){ return reject(new TypeError('循環引用了!')); } /** 8 一個標示 表示當前沒有被調用過 * 確保resolve或者reject後的狀態不會再次發生變動 */ let called; /** 5 保證promise3是一個引用類型 * 判斷新返回值promise3的類型 * 若是是普通值常量 就直接resolve導出 */ if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ let then = promise3.then; /** 6 確保promise3是一個Promise * 判斷promise3的then方法 * 若是存在 而且是一個function類型 * 就表示promise3是一個Promise */ if(typeof then === 'function'){ /** 9 執行promise3的then方法 * 由於promise3也是一個Promise * 須要再次解析promise3的then方法 * 直到解析到最後的返回值不是一個Promise類型爲止 */ then(promise3, (promise4)=>{ /** 同8 */ if(called) return; called = true; /** 10 遞歸解析新的返回值的類型 * 解析到返回值不是一個Promise類型爲止 */ resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ /** 同8 */ if(called) return; called = true; reject(r); }); }else{ /** 7 此時promise3是一個普通對象 直接resolve() */ resolve(promise3); } }catch(e){ /** 同8 */ if(called) return; called = true; reject(e); }; }else{ /** 同5 普通值直接resolve()*/ resolve(promise3); } } module.exports = Promise;
ok. 至此 咱們已經實現了Promsie的異步和鏈式調用. Promise中比較複雜的部分咱們已經搞定了 接下來就是添加一些方法,其實這部分反而沒那麼複雜了.
catch : catch方法本質上就是一個then方法的變形,只有失敗時的回調 沒有成功時的回調
finally : finally方法的做用是無論 Promise 對象最後狀態如何,都會執行操做.其實說白了就是在then方法的成功和失敗的回調函數中都執行該方法就好了.
ok.老規矩 上代碼~
增長catch finally方法
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ /** 2 此處有個坑 若是隻寫1 不寫2的話 * 會報一個TypeError :onRejected is not a function * 在此處給它一個默認的成功和失敗的回調函數就好 */ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 === promise3){ return reject(new TypeError('循環引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ let then = promise3.then; if(typeof then === 'function'){ then(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } /** 1 直接返回this的then方法 * 由於catch只捕獲錯誤 因此resolve直接爲null * 返回reject就好*/ Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } /** 3 finally實現起來也很簡單 * 分別在resolve和reject中執行fn就好 * 最後再把this返回出去就好 */ Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } module.exports = Promise;
增長all race resolve reject等方法
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 === promise3){ return reject(new TypeError('循環引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ let then = promise3.then; if(typeof then === 'function'){ then(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } /** 1 直接在構造函數上增長all方法 * 它返回的也是一個Promise * 等待參數數組中全部的promise都執行完畢後 * 再返回結果 */ Promise.all = function(values){ return new Promise((resolve,reject)=>{ /** 2 定義一個存放最終結果的數組result和一個index */ let results = []; let index = 0; /** 3 定義一個方法addToArr() * 讓index每次執行增長results數組元素的函數的時候都+1 * 當index === values的長度的時候 說明此時全部promsie都執行完畢並放到的數組中 * 而後直接resolve(results)就好了 */ function addToArr(key,value){ index++; results[key] = value; /** 6 當知足條件時 說明全部的promise都執行完畢 直接resolve(results) */ if(index === values.length){ resolve(results); } } /** 4 循環values中的每一項promsie */ for(let i = 0; i < values.length; i++){ let current = values[i]; /** 5 判斷每一項promise的返回值是否是一個Promsie * 是的話就執行該Promise的then方法 拿到返回值 並放到數組results中 * 是一個普通值的話就直接將該值放到數組results中 */ if(current && current.then && typeof current.then === 'function'){ current.then((value)=>{ /** 同5 把返回值放到數組results中*/ addToArr(i,value); },reject); }else{ /** 同5 把返回值放到數組results中*/ addToArr(i,current); } } }); } /** race方法相比較於all方法簡單不少 * 由於race中的promsie成功resolve一個 * 整個race就resolve */ Promise.race = function(values){ return new Promise((resolve,reject)=>{ /** 同4 */ for(let i = 0; i < values.length; i++){ let current = values[i]; /** 同5 */ if(current&¤t.then&&typeof current.then === 'function'){ /** 7 直接執行then就好 */ current.then(resolve,reject); }else{ /** 8 普通值直接resolve */ resolve(current); } } }); } // resolve方法 Promise.resolve = function(value){ return new Promise((resolve,reject)=>{ resolve(value); }); } // reject方法 Promise.reject = function(reason){ return new Promise((resolve,reject)=>{ reject(reason); }); } module.exports = Promise;
實現一個promise的延遲對象defer
此步是爲了測試咱們手寫的Promsie符不符合Promsie/A+規範,若是沒有defer的話,咱們在測試過程當中就會報一個TypeError: adapter.deferred is not a function.
其實寫完defer後,咱們就能夠去進行測試咱們手寫的Promsie符不符合Promsie/A+規範了。
即:本篇手寫一款符合Promise/A+規範的Promise的最終本爲:
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } try{ executor(resolve,reject); }catch(e){ reject(e); } } Promise.prototype.then = function(onFilfulled,onRejected){ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 == promise3){ return reject(new TypeError('循環引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object' || typeof promise3 === 'function')){ try{ let then = promise3.then; if(typeof then === 'function'){ then.call(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } Promise.all = function(values){ return new Promise((resolve,reject)=>{ let results = []; let index = 0; function addToArr(key,value){ index++; results[key] = value; if(index === values.length){ resolve(results); } } for(let i = 0; i < values.length; i++){ let current = values[i]; if(current && current.then && typeof current.then === 'function'){ current.then((value)=>{ addToArr(i,value); },reject); }else{ addToArr(i,current); } } }); } Promise.race = function(values){ return new Promise((resolve,reject)=>{ for(let i = 0; i < values.length; i++){ let current = values[i]; if(current&¤t.then&&typeof current.then === 'function'){ current.then(resolve,reject); }else{ resolve(current); } } }); } Promise.resolve = function(value){ return new Promise((resolve,reject)=>{ resolve(value); }); } Promise.reject = function(reason){ return new Promise((resolve,reject)=>{ reject(reason); }); } // 實現一個promise的延遲對象 defer Promise.defer = Promise.deferred = function(){ let dfd = {}; dfd.promise = new Promise((resolve, reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } module.exports = Promise;
最終測試
源碼在github上,寫文章不易,歡迎star或fork,thx~
github