Promise 你們都知道怎麼用, 可是對於內部的原理不少人都不是很清楚 javascript
來看一個面試題: Promise的then 是怎麼實現的java
首先來分析一下then面試
來一個一個看吧promise
Promise.prototype.then = function(){}
Promise.prototype.then = function(onFulfilled,onRejected){}
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise(function(resolve,reject){ // 代碼省略 }) }
要實現promise鏈式 返回的必須是一個Promise 再因爲status 改變狀態了不能再變 因此須要第二個.... 第N個promise 調用新的resolve異步
Promise.prototype.then = function(onFulfilled,onRejected){ var self = this; // 保存當前上下文 return new Promise(function(resolve,reject){ if(self.status === 'resolved'){ onFulfilled(self.res) } if(self.status === 'rejected'){ onRejected(self.err) } }) }
Promise.resolve(res) 、、 同步代碼的執行狀況下 上述邏輯成立函數
res 跟err 來源以下this
function Promise(executor){ let self = this this.status = 'pending' // Promise 的狀態值 this.res = undefined // 存成功以後的值 this.err = undefined // 存失敗以後的值 executor(resolve,reject) // 省略代碼 }
executor 有2個參數 分別爲resolve,reject
當調用prototype
new Promise((resolve,reject)=>{ setTimeout(()=>{ if(true){ resolve('res') }else{ reject('err') } },1000) })
executor 會執行 而且把成功的值或者失敗的值拋出來,resolve跟reject也是2個函數 定義在Promise內部線程
function Promise(executor){ // ...代碼省略 function resolve(res){ self.res = res } function reject(err){ self.err = err } executor(resolve,reject) }
resolve 跟reject 還須要改變 Promise的狀態值 而且一旦發生改變以後不能再次改變code
function Promise(executor){ // 代碼省略 function resolve(res){ if(self.status === 'pending'){ self.status = 'resolved' self.res = res } } function reject(err){ if(self.status === 'pending'){ self.status = 'rejected' self.err = err } } executor(resolve,reject) }
咱們在executor中操做的每每是異步代碼, 這個以後直接then, status的狀態值並未發生改變, 因此不會執行onFulfilled跟onRejected,
這個時候咱們須要訂閱
function Promise(executor){ // 代碼省略 this.onFulfilledCallback = [] // 存成功以後的回調 this.onrejectedCallback = [] // 存失敗以後的回調 function resolve(res){ if(self.status === 'pending'){ self.status = 'resolved' self.res = res } } function reject(err){ if(self.status === 'pending'){ self.status = 'rejected' self.err = err } } executor(resolve,reject) } new Promise(executor).then((onFulfilled,onrejected)=>{ var self = this; // 保存當前上下文 **注意: 第二次調用then this是指向new Promise的** return new Promise(function(resolve,reject){ // ...代碼省略 if(self.status === 'pending'){ self.onFulfilledCallback.push(function(){ // 把成功以後須要作的事存起來 有多少個then就有多少個函數 onFulfilled(self.res) // 注意 這裏的self.res 會隨着then的調用發生改變 由於每次then都new 了一個Promise }) self.onrejectedCallback.push(function(){ // 把失敗以後的事存起來 有多少個then就有多少個函數 onFulfilled(self.err) }) } }) })
當resolve的時候 或者reject的時候
一一拿出來執行
function resolve(res){ // 省略代碼 self.res = res onFulfilledCallback.forEach(fn=>{ fn() }) }
實現鏈式調用咱們須要對then函數執行的返回值作一個操做!!!
需求:
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise((resolve,reject)=>{ // ...代碼省略 if(self.status === 'resolved'){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這裏執行Promise.resolve() 同步代碼 // 而後把x傳遞給下一個then resolve(x) } }) }
若是resolve是異步的話
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise((resolve,reject)=>{ // ...代碼省略 if(self.status === 'resolved'){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這裏執行的是Promise.resolve() 同步代碼 // 而後把x傳遞給下一個then resolve(x) } if(self.status === 'pending'){ self.onFulfilledCallback.push(function(){ let x = onFulfilled(self.res) // 這裏的self.res 是上一個new Promise上的值 此時的onFUlfilled 至關於 fn(){let x = onFulfilled} resolve(x) }) } }) } // 同時爲了拿到 fn(){let x = onFulfilled ...} 得值 傳遞給下一個onFulfilled,onFulfilledCallback須要改寫 onRejectedCallback同理 onFulfilledCallback.forEach(fn=>{ return fn() }) onRejectedCallback.forEach(fn=>{ return fn() })
-
考慮以下狀況:
1. 返回值是個promise
Promsie.resolve(11).then(res=>{ return new Promise(executor) })
2. 返回值是個普通值
Promsie.resolve(11).then(res=>{ return 1 })
最關鍵的來了
咱們須要對onFullfillled 的返回值進行判斷
修改then函數
// ...代碼省略 if(self.status === 'resolved'){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這裏執行的是Promise.resolve() 同步代碼 // 而後把x傳遞給下一個then promiseResolve(x,resolve,reject) }
function promiseResolve(x,resolve,reject){ if((typeof x != null && typeof x == 'object') || typeof x == 'function'){ // 肯定是個引用類型 if (x instanceof Promise){ // 肯定是個Promise 實例 let then = x.then then.call(x,res=>{ // 保證x調用res=>{}, res=>{}是then的onFulfilled方法 一直判斷直到onFulfilled 返回的是個普通值 而後resolve出去 promiseResolve(res,resolve,reject) },err=>{ reject(err) }) } }else{ resolve(x) } }
考慮這樣極端問題:
let p = new Promise(resolve=>{ resolve(3333) }) let p2 = p.then(resolve=>{ return p2 })
這樣會出現什麼問題呢, onFulfilled的返回結果若是是一個promise 上面的遞歸調用已經說明了 咱們要不斷的去遞歸最終的onFulfilled結果 而後再改變p2的status 這樣變成了p2去等待p2的執行結果 函數死掉了
因此onFulfilled的返回結果須要跟then的返回結果去比較 修改函數
function promiseResolve(x,resolve,reject,promise){ if(x === promise ){ return new Error('引用錯誤!') } if((typeof x != null && typeof x == 'object') || typeof x == 'function'){ // 肯定是個引用類型 if (x instanceof Promise){ // 肯定是個Promise 實例 let then = x.then then.call(x,res=>{ // 保證x調用res=>{}, res=>{}是then的onFulfilled方法 一直判斷直到onFulfilled 返回的是個普通值 而後resolve出去 promiseResolve(res,resolve,reject) },err=>{ reject(err) }) } }else{ resolve(x) } }
Promise.prototype.then = function(onFulfilled,onRejected){ // ...代碼省略 let promise2 = new Promise((resolve,reject)=>{ if(self.status === 'pending'){ self.onFulfilledCallback.push(function(){ let x = onFulfilled(self.res) // 這裏的self.res 是上一個new Promise上的值 此時的onFUlfilled 至關於 fn(){let x = onFulfilled} promiseResolve(x,resolve,reject,promise2) }) } }) return promise2 }
這段代碼仍是有錯誤, 因爲javascript主線程是單線程的, 因此在then裏的promiseResolve是拿不到promise2的 因此咱們須要開啓異步 使用定時器或者nextTick 這裏咱們用定時器
Promise.prototype.then = function(onFulfilled,onRejected){ // ...代碼省略 let promise2 = new Promise((resolve,reject)=>{ if(self.status === 'pending'){ self.onFulfilledCallback.push(function(){ setTimeout(()=>{ let x = onFulfilled(self.res) // 這裏的self.res 是上一個new Promise上的值 此時的onFUlfilled 至關於 fn(){let x = onFulfilled} promiseResolve(x,resolve,reject,promise2) },0) }) } }) return promise2 }
此時 整個then差很少完成了
then每次建立一個新的promise對象 對於同步的resolve,reject直接調用onFulfilled或者onRejected ,對於異步的resolve,reject使用
訂閱發佈模式,把每一個resolve,reject 暫存起來 等達到條件時候一一執行, 而且拿到返回結果去跟內部的promise比較,而且判斷若是是一個promise的話,不斷解析onFulfilled 的返回結果 直至resolve出去