Promise原理實現


首先先看一下 promise 的調用方式:

// 實例化 Promise:
  new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)  //這里相當於給value賦值
    }, 0)
  }).then(value => {
    console.log(value)
  })

實現原理如下:

const PENDING = 'pending'  //首先我們創建了三個常量用於表示狀態,對於經常使用的一些值都應該通過常量來管理,便於開發及后期維護
const RESOLVED = 'resolved' 
const REJECTED = 'rejected' 

function MyPromise(fn) {
  const that = this  //在函數體內部首先創建了常量 `that`,因為代碼可能會異步執行,用於獲取正確的 `this` 對象
  that.state = PENDING   //一開始 `Promise` 的狀態應該是 `pending`
  that.value = null    //`value` 變量用於保存 `resolve` 或者 `reject` 中傳入的值
  that.resolvedCallbacks = []  
  that.rejectedCallbacks = []
  /*
  `resolvedCallbacks` 和 `rejectedCallbacks` 用於保存 `then` 中的回調,
  因為當執行完 `Promise` 時狀態可能還是等待中,這時候應該把 `then` 中的回調保存起來用於狀態改變時使用
  */
  function resolve(value) {
    if (that.state === PENDING) {
      that.state = RESOLVED
      that.value = value
      that.resolvedCallbacks.map(cb => cb(that.value))//map這里是執行回調函數
    }
  }
  
  function reject(value) {
    if (that.state === PENDING) {
      that.state = REJECTED
      that.value = value
      that.rejectedCallbacks.map(cb => cb(that.value))//map這里是執行回調函數
    }
  }
/*
*   首先兩個函數都得判斷當前狀態是否為等待中,因為規范規定只有等待態才可以改變狀態
*   將當前狀態更改為對應狀態,並且將傳入的值賦值給 `value`
*   遍歷回調數組並執行
*/
    try {
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }

}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    const that = this
    //首先判斷兩個參數是否為函數類型,因為這兩個參數是可選參數
    //當參數不是函數類型時,需要創建一個函數賦值給對應的參數,同時也實現了透傳
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => { return v }
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : r => {
            throw r
          }
    //接下來就是一系列判斷狀態的邏輯,當狀態不是等待態時,就去執行相對應的函數。
    //如果狀態是等待態的話,就往回調函數中 `push` 函數,比如如下代碼就會進入等待態的邏輯
    if (that.state === PENDING) {
      that.resolvedCallbacks.push(onFulfilled)//push這里是保存回調函數
      that.rejectedCallbacks.push(onRejected)
    }
    if (that.state === RESOLVED) {
      onFulfilled(that.value)
    }
    if (that.state === REJECTED) {
      onRejected(that.value)
    }
}

詳細解釋如下:

定義異步函數 MyPromise,所以執行的函數也是 MyPromise:
首先看 函數執行的方法:
new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  }, 0)
})
函數的參數是:
(resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  }, 0)
}
對應着
function MyPromise(fn){
    try {
      fn(resolve, reject) // 在這里執行了傳入的參數fn(),並且把規定的 
      resolve, reject 兩個參數傳遞到 fn 中。
    } catch (e) {
    reject(e)
    }
}
中的 fn,所以會執行這個傳入的函數 fn(resolve, reject);
傳入的參數是異步的,會在同步代碼結束后再去執行對應的 resolve(1)這個函數,
而這個函數已經在 MyPromise 中進行了定義:
function resolve(value) {
    if (that.state === PENDING) {
      that.state = RESOLVED
      that.value = value
      that.resolvedCallbacks.map(cb => cb(that.value))//map這里是執行回調函數
    }
}
// 也就是會在同步代碼之后再執行上面的函數,所以我們繼續看 MyPromise 的調用
new MyPromise((resolve, reject) => {
  //異步代碼
}).then(value => {
  console.log(value)
})
接着執行方法: then()。而then 掛在了原型鏈上:
MyPromise.prototype.then = function(onFulfilled, onRejected) {  }
所以then包含兩個參數,分別對對應 onFulfilled, onRejected。 如果沒有定義需要做容錯處理,
也就是給默認的函數參數:  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => { return v } 
由於執行同步操作,此時state 還等於PENDING,所以執行:
 
if (that.state === PENDING) {
  that.resolvedCallbacks.push(onFulfilled)//push這里是保存回調函數
  that.rejectedCallbacks.push(onRejected)
}
也就是把 成功后操作函數和失敗函數分別保存到對應的數組中。
setTimeout(() => {
    resolve(1)
}, 0)
好了,接下來同步執行結束,然后開始執行異步操作:
function resolve(value) {
  if (that.state === PENDING) {
    that.state = RESOLVED
    that.value = value
    that.resolvedCallbacks.map(cb => cb(that.value))//map這里是執行回調函數
  }
}
可以看到,先改變狀態,再從保存數組中,獲取到回調函數,再執行!
至此,在promise中 resolve(1) 告訴了執行回調的時機和參數。
而then規定的是異步之后的回調函數。
然后我們看到還在then函數中規定了其他的狀態,解釋一下:
如果執行函數中,沒有異步處理:
new MyPromise((resolve, reject) => {
  resolve(1)
}).then(value => {
  console.log(value)
})
也就是在定義中:
try {
    fn(resolve, reject)
} catch (e) {
    reject(e)
}
直接執行了 fn(),根據傳入的 resolve,reject。這里直接執行了 resolve:
function resolve(value) {
  if (that.state === PENDING) {
    that.state = RESOLVED
    that.value = value
    that.resolvedCallbacks.map(cb => cb(that.value))//map這里是執行回調函數
  }
}
更改了狀態,由於還沒有執行then函數,保存函數沒有數據,所以沒有可以執行的回調函數。
接下來程序走到了then
new MyPromise((resolve, reject) => {
  //同步代碼
}).then(value => {
  console.log(value)
})
由於狀態已經改變,所以執行:
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  if (that.state === RESOLVED) {
    onFulfilled(that.value)
  }
}
同樣也做到了執行then傳入的函數。
總結一下,如果 resolve 被放在了異步函數中,then傳入的回調函數會先被保存下來,待異步函數執行完畢之后,
在次執行回調函數;
而如若 resolve 被放在了同步函數中,則回調函數數組為空,順序執行到 then 函數,則會執行該回調函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM