簡單版 Promise/A+,通過官方872個測試用例


promise 標准

在實現 Promise 之前要清楚的是 JavaScript 中的 Promise 遵循了 Promises/A+ 規范,所以我們在編寫 Promise 時也應當遵循這個規范,建議認真、仔細讀幾遍這個規范。最好是理解事件循環,這樣對於理解js中的異步是怎么回事非常重要。

https://promisesaplus.com/

基本使用

new Promise( function(resolve, reject) {...} /* executor */  );
new Promise((resolve, reject)=> { AjaxRequest.post({ url: 'url', data: {}, sueccess: ()=> { resolve(res) }, fail: (err)=> { reject(err) } }) }).then((res)=> { // do some }).then(value => { }).catch((err)=> { // do some })

promise 是處理異步結果的一個對象,承若狀態改變時調用對應的回調函數,resolve、reject用來改變promise 的狀態,then 綁定成功、失敗的回調。

環境准備

安裝測試工具以及nodemon因為我們要在node環境調試自己寫的promise

// nodemon
npm install nodemon -D
// promise 測試工具
npm install promises-aplus-tests -D 

增加腳本命令

"testPromise": "promises-aplus-tests myPromise/promise3.js",
 "dev": "nodemon ./myPromise/index.js -i "

各自的路徑改成自己的即可,這個在后面會用來測試。

基本架子

根據規范實現一個簡單的promise,功能如下

  1. promise的三種狀態(PENDING、FULFILLED、REJECTED)
  2. 狀態只能由 Pending 變為 Fulfilled 或由 Pending 變為 Rejected ,且狀態改變之后不會在發生變化,會一直保持這個狀態
  3. 綁定then的回調
  4. 返回成功、失敗的值
  5. 一個promise 支持調用多次then
  6. 支持捕獲異常
/* 

基本架子
根據promise A+ 規范還要處理then鏈式調用以及返回值傳遞的問題,后續在promise2、promise3 處理

*/

const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';

class myPromise {
  constructor (executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
this.onResolveCallbacks = [] this.onRejectedCallbacks = [] const resolve = (value) => { if (this.status === PENDING) { this.status = FULFILLED this.value = value // 發布 this.onResolveCallbacks.forEach(fn => fn()) } } const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED this.reason = reason // 發布 this.onRejectedCallbacks.forEach(fn => fn()) } } try { // 執行傳進來的fn, 在給他提供改變狀態的fn executor(resolve, reject) } catch(e) { reject(e) } } // 訂閱回調函數 then (onFulfilled, onRejected) { if (this.status = PENDING) { // 訂閱 this.onResolveCallbacks.push(() => { onFulfilled(this.value) }) this.onRejectedCallbacks.push(() => { onRejected(this.reason) }) } if (this.status === FULFILLED) { onFulfilled(this.value) } if (this.status === REJECTED) { onRejected(this.reason) } } } module.exports = myPromise

訂閱

   傳進來的fn是一個執行器,接受resolve、reject參數,通常我們在構造函數中需要調用某個接口,這是一個異步的操作,執行完構造函數之后,在執行then(),這個時候的狀態還是pending,所以我們需要把then 綁定的回調存起來,也可以理解為promise對象訂閱了這個回調。

發布

   在 resolve,reject函數中中我們改變了promise 對象的狀態,既然狀態改變了,那么我們需要執行之前訂閱的回調,所以在不同的狀態下執行對應的回調即可。

流程

如上所示,實例化對象,執行構造函數,碰到異步,掛起,然后執行then()方法,綁定了resolve、reject的回調。如果異步有了結果執行對應的業務邏輯,調用resolve、或者reject,改變對應的狀態,觸發我們綁定的回調。

以上就是最基本的promise架子,但是還有promise 調用鏈沒有處理,下面繼續完善...

完善promise 調用鏈

promose 的精妙的地方就是這個調用鏈,首先then 函數會返回一個新的promise 對象,並且每一個promise 對象又有一個then 函數。驚不驚喜原理就是那么簡單,回顧下then的一些特點

then 特點

  1. then 返回一個新的promise 對象
  2. then 綁定的回調函數在異步隊列中執行(evnet loop 事件循環)
  3. 通過return 來傳遞結果,跟fn一樣如果沒有return,默認會是 underfined
  4. 拋出異常執行綁定的失敗函數(最近的promise),如果沒有,則執行catch
  5. then中不管是不是異步只要resolve、rejected 就會執行對應 onFulfilled、onRejected 函數
  6. then中返回promise狀態跟執行回調的結果有關,如果沒有異常則是FULFILLED,就算沒有retun 也是FULFILLED,值是underfined,有異常就是REJECTED,接着走下個then 綁定的onFulfilled 、onRejected 函數

   根據上面的特點以及閱讀規范我們知道then()函數主要需要處理以下幾點

  • 返回一個新的promise
  • 值怎么傳給then返回的那個promise
  • 狀態的改變

返回一個新的promise

因為promise 的鏈式調用涉及到狀態,所以then 中返回的promise 是一個新的promise

then(onFulfilled, onRejected) {
   let promise2 = new Promise((resolve, reject) => {
     // do ...
   })
   return promise2
 }

值的傳遞、狀態的改變

let p = new myPromise((resolve, rejected) => {
  // do ...
})
p.then(
  value => {
    return 1
  },
  reason => {}
  )
  .then(
    value => {
      return new Promise((resolve, rejected) => {
        resolve('joel')
      })
    },
    reason => {}
    )
  .then(
    value => {
      throw 'err: 出錯啦'
    },
    reason => {}
    )

then 返回的值可能是一個普通值、promise對象、function、error 等對於這部分規范文檔也有詳細的說明

image.png

[[Resolve]](promise, x)

這個可以理解為promise 處理的過程,其中x是執行回調的一個值,promise 是返回新的promise對象,完整代碼如下

我們將這部分邏輯抽成一個獨立的函數 如下

// 處理then返回結果的流程
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<myPromise>'))
  }

  let called = false

  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    try {
      let then = x.then
      // 判斷是否是promise
      if (typeof then === 'function') {
        then.call(x, (y) => {
          // 如果 resolvePromise 以值 y 為參數被調用,則運行 [[Resolve]](promise, y)
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, (r) => {
          if (called) return
          called = true
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    // 如果 x 不為對象或者函數,以 x 普通值執行回調
    resolve(x)
  }
}

測試

promises-aplus-tests 這個工具我們必須實現一個靜態方法deferred,官方對這個方法的定義如下:

deferred: 返回一個包含{ promise, resolve, reject }的對象

promise 是一個處於pending狀態的promise

resolve(value) 用value解決上面那個promise

reject(reason) 用reason拒絕上面那個promise

添加如下代碼

myPromise.defer = myPromise.deferred = function () {
  let deferred = {}

  deferred.promise = new myPromise((resolve, reject) => {
    deferred.resolve = resolve
    deferred.reject = reject
  })
  return deferred
}

在編輯執行我們前面加的命令即可

npm run testMyPromise

image.png

 

完善其他方法

  1. all
  2. allSettled
  3. any
  4. race
  5. catch
  6. finlly
npm run dev // 可以用來測試這些方法

image.png

源碼

源碼

比較官方的源碼: https://github.com/then/promise 

參考

https://promisesaplus.com/

https://www.jianshu.com/p/4d266538f364

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

 

 


免責聲明!

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



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