實(shí)現(xiàn) Promise

本文主要實(shí)現(xiàn) Promise 的 resolve治专,reject,以及 then 方法的鏈?zhǔn)秸{(diào)用配猫,并將會(huì)對(duì)完成的代碼進(jìn)行測(cè)試,最終實(shí)現(xiàn)下面的效果

new Promise((resolve,reject)=>{
  resolve()
  reject()
})
.then(res=>{console.log(res),(err)=>{console.log(err)}}
)

初步實(shí)現(xiàn)Promise

  1. 創(chuàng)建 Promise 類,添加resolve,reject 方法
class Promise {
    constructor(executor){
     if(typeof executor !== 'function'){
        // 參數(shù)校驗(yàn)技即,參數(shù)必須是函數(shù)
        throw new TypeError(`Promise resolver ${executor} is not a function`)
     }
     // 定義 resolve 函數(shù)和 reject 函數(shù)
     const resolve = function(){}
     const reject = function(){}
    // 執(zhí)行傳入的函數(shù) 
    executor(resolve,reject)
    }
}
module.exports = Promise; 
  1. 完善 resolve,reject 方法
class Promise {
    constructor(executor){
     if(typeof executor !== 'function'){
        // 參數(shù)校驗(yàn)樟遣,參數(shù)必須是函數(shù)
        throw new TypeError(`Promise resolver ${executor} is not a function`)
     }
    // 初始化值
     this.value = null
     this.reason = null
     this.status = 'pending'
     const resolve = (value)=>{
         // 成功后的操作:改變狀態(tài)而叼,成功后執(zhí)行回調(diào)
         if(this.status === 'pendding'){
             this.status = 'fulfilled'
             this.value = value
         }
     }
     const reject = (reason)=>{
         // 失敗后的操作:改變狀態(tài),失敗后執(zhí)行回調(diào)
         if(this.status === 'pendding'){
            this.status = 'rejected'
            this.reason = reason
        }
     }
    // 執(zhí)行傳入的函數(shù) 
    executor(resolve,reject)
    }
} 

優(yōu)化代碼豹悬,綁定 this

class Promise {
  constructor(executor){
       if (typeof executor !== 'function') {
          throw new TypeError(`Promise resolver ${executor} is not a function`)
      }
     // 初始化值葵陵,綁定 this,執(zhí)行傳入的回調(diào)
     this.initialValue()
      this.initBind()
// 防止發(fā)生錯(cuò)誤
       try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
  }
  initBind() {
      this.resolve = this.resolve.bind(this)
      this.reject = this.reject.bind(this)
  }
  initialValue() {
      // 初始化值
      this.value = null
      this.reason = null
      this.status = Promise.PENDING 
  }
  // 定義 resolve 函數(shù)和 reject 函數(shù)
  resolve(value) {
      // 成功后的操作:改變狀態(tài)瞻佛,成功后執(zhí)行回調(diào)
      if (this.status === Promise.PENDING) {
          this.status = Promise.FULFILLED 
          this.value = value
      }
  }
  reject(reason) {
      // 失敗后的操作:改變狀態(tài)脱篙,失敗后執(zhí)行回調(diào)
      if (this.status === Promise.PENDING) {
          this.status = Promise.REJECTED
          this.reason = reason
  }
}
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'

初步實(shí)現(xiàn) then 方法

在 promise 中,如果 then 方法未傳入?yún)?shù)伤柄,在接下來的 then 方法中依然是可以獲取結(jié)果的

new Promise((resolve,reject) => {
  resolve(123)
})
.then()
.then(res=>{
  console.log(res) // 輸出 123
})

想要實(shí)現(xiàn)值得穿透效果绊困,只需要每次將值傳出就可以了

then(onFulfilled, onRejected) {
  // 值的穿透問題,參數(shù)校驗(yàn)
  if (typeof onFulfilled !== 'function') {
      onFulfilled = function (value) {
          return value
      }
  }
  if (typeof onRejected !== 'function') {
      onRejected = function (reason) {
          throw reason
      }
  }
  if (this.status === Promise.FULFILLED) {
      onFulfilled(this.value)
  }
  if (this.status === Promise.REJECTED) {
      onRejected(this.reason)
  }
}

實(shí)現(xiàn) then 方法的異步

then(onFulfilled, onRejected) {
  // 值的穿透問題,參數(shù)校驗(yàn)
  if (typeof onFulfilled !== 'function') {
      onFulfilled = function (value) {
          return value
      }
  }
  if (typeof onRejected !== 'function') {
      onRejected = function (reason) {
          throw reason
      }
  }
  if (this.status === Promise.FULFILLED) {
     // 異步
       setTimeout(() => {
                onFulfilled(this.value)
            })
  }
  if (this.status === Promise.REJECTED) {
      setTimeout(() => {
                onRejected(this.reason)
            })
  }
}

將then 中的回調(diào)添加到數(shù)組

class Promise {
    constructor(executor) {
        if (typeof executor !== 'function') {
            throw new TypeError(`Promise resolver ${executor} is not a function`)
        }
        this.initialValue()
        this.initBind()
        // 原生 promise 中異常處理在 reject 函數(shù)中
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }

    }
    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    initialValue() {
        // 初始化值
        this.value = null
        this.reason = null
        this.status = Promise.PENDING
        // 添加成功和時(shí)報(bào)回調(diào)
       + this.onFulfilledCallbacks = []
       + this.onRejectedCallbacks = []
    }
    // 定義 resolve 函數(shù)和 reject 函數(shù)
    resolve(value) {
        // 成功后的操作:改變狀態(tài),成功后執(zhí)行回調(diào)
        if (this.status === Promise.PENDING) {
            this.status = Promise.FULFILLED
            this.value = value
            + this.onFulfilledCallbacks.forEach(fn => {
            +   fn(this.value)
            + })
        }
    }
    reject(reason) {
        // 失敗后的操作:改變狀態(tài)适刀,失敗后執(zhí)行回調(diào)
        if (this.status === Promise.PENDING) {
            this.status = Promise.REJECTED
            this.reason = reason
          +  this.onRejectedCallbacks.forEach(fn => {
          +     fn(this.reason)
          +  })
        }
    }
    then(onFulfilled, onRejected) {
        // 值得穿透問題,參數(shù)校驗(yàn)
        if (typeof onFulfilled !== 'function') {
            onFulfilled = function (value) {
                return value
            }
        }
      + if (typeof onRejected !== 'function') {
      +     onRejected = function (reason) {
      +          throw reason
      +     }
        }
        // 實(shí)現(xiàn)異步操作
      +  if (this.status === Promise.FULFILLED) {
      +     setTimeout(() => {
      +          onFulfilled(this.value)
      +      })
      +    }
      +  if (this.status === Promise.REJECTED) {
     +        setTimeout(() => {
     +            onRejected(this.reason)
     +       })

      +  }
        // pending 狀態(tài)下將要執(zhí)行的函數(shù)放到數(shù)組中
      +  if (this.status === Promise.PENDING) {
      +     this.onFulfilledCallbacks.push((value) => {
      +         setTimeout(()=>{
      +              onFulfilled(value)
      +        })
      +       })
      +      this.onRejectedCallbacks.push(reason=>{
      +          setTimeout(()=>{
      +              onRejected(reason)
      +          })
     +       })
        }
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'

實(shí)現(xiàn) then 方法返回一個(gè)新的 promise秤朗,可以在 resolve 中返回一個(gè)新的promise

class Promise {
  constructor(executor) {
    if (typeof executor !== 'function') {
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    this.initialValue()
    this.initBind()
    // 原生 promise 中異常處理在 reject 函數(shù)中
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }

  }
  initBind() {
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
  }
  initialValue() {
    // 初始化值
    this.value = null
    this.reason = null
    this.status = Promise.PENDING
    // 添加成功和時(shí)報(bào)回調(diào)
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
  }
  // 定義 resolve 函數(shù)和 reject 函數(shù)
  resolve(value) {
    // 成功后的操作:改變狀態(tài),成功后執(zhí)行回調(diào)
    if (this.status === Promise.PENDING) {
      this.status = Promise.FULFILLED
      this.value = value
      this.onFulfilledCallbacks.forEach(fn => {
        fn(this.value)
      })
    }
  }
  reject(reason) {
    // 失敗后的操作:改變狀態(tài)笔喉,失敗后執(zhí)行回調(diào)
    if (this.status === Promise.PENDING) {
      this.status = Promise.REJECTED
      this.reason = reason
      this.onRejectedCallbacks.forEach(fn => {
        fn(this.reason)
      })
    }
  }

  then(onFulfilled, onRejected) {
    // 值的穿透問題,參數(shù)校驗(yàn)
    if (typeof onFulfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }
    if (typeof onRejected !== 'function') {
      onRejected = function (reason) {
        throw reason
      }
    }

    // 實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用取视,且改變后面的 then 的值,必須通過新的實(shí)例
    let promise2 = new Promise((resolve, reject) => {

      // 實(shí)現(xiàn)異步操作
      if (this.status === Promise.FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            Promise.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }

        })

      }
      if (this.status === Promise.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            Promise.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }

        })

      }
      // pending 狀態(tài)下將要執(zhí)行的函數(shù)放到數(shù)組中
      if (this.status === Promise.PENDING) {
        this.onFulfilledCallbacks.push((value) => {
          setTimeout(() => {
            try {
              let x = onFulfilled(value)
              Promise.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }

          })
        })
        this.onRejectedCallbacks.push(reason => {
          setTimeout(() => {
            try {
              let x = onRejected(reason)
              Promise.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }

          })
        })
      }

    })

    return promise2

  }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'


Promise.resolvePromise = function (promise2, x, resolve, reject) {
  // 避免鏈?zhǔn)叫?yīng)
  if (promise2 === x) {
    reject(new TypeError('Chaining circle detected for promise'))
  }
  let called = false
  if (x instanceof Promise) {
    // 判斷 x 是否為 promise
    x.then(value => {
      Promise.resolvePromise(promise2, value, resolve, reject)
    }, reason => {
      reject(reason)
    })
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 判斷 x 是否為對(duì)象或者函數(shù)
    try {
      const then = x.then
      if (typeof then === 'function') {
        then.call(x, value => {
          if (called) return
          called = true
          Promise.resolvePromise(promise2, value, resolve, reject)
        }, reason => {
          if (called) return
          called = true
          reject(reason)
        })
      } else {
        if (called) return
        called = true
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }

  } else {
    resolve(x)
  }
}

測(cè)試

  1. 安裝測(cè)試工具
npm install promises-aplus-tests 
// 用來測(cè)試自己的promise 符不符合promisesA+規(guī)范,mac用戶最前面加上sudo
  1. 添加測(cè)試代碼
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve,reject)=>{
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
module.exports = Promise;
  1. 運(yùn)行測(cè)試代碼
  promises-aplus-tests [js文件名] 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末常挚,一起剝皮案震驚了整個(gè)濱河市作谭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奄毡,老刑警劉巖折欠,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡怨酝,警方通過查閱死者的電腦和手機(jī)傀缩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來农猬,“玉大人赡艰,你說我怎么就攤上這事〗锎校” “怎么了慷垮?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)揍堕。 經(jīng)常有香客問我料身,道長(zhǎng),這世上最難降的妖魔是什么衩茸? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任芹血,我火速辦了婚禮,結(jié)果婚禮上楞慈,老公的妹妹穿的比我還像新娘幔烛。我一直安慰自己,他們只是感情好囊蓝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布饿悬。 她就那樣靜靜地躺著,像睡著了一般聚霜。 火紅的嫁衣襯著肌膚如雪狡恬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天蝎宇,我揣著相機(jī)與錄音弟劲,去河邊找鬼。 笑死夫啊,一個(gè)胖子當(dāng)著我的面吹牛函卒,可吹牛的內(nèi)容都是我干的辆憔。 我是一名探鬼主播撇眯,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼虱咧!你這毒婦竟也來了熊榛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤腕巡,失蹤者是張志新(化名)和其女友劉穎玄坦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡煎楣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年豺总,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片择懂。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喻喳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出困曙,到底是詐尸還是另有隱情,我是刑警寧澤慷丽,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布要糊,位于F島的核電站纲熏,受9級(jí)特大地震影響锄俄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜珊膜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剔氏。 院中可真熱鬧竹祷,春花似錦谈跛、人聲如沸塑陵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嫂沉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趟章,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工蚓土, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜀漆。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像旧蛾,于是被迫代替她去往敵國(guó)和親蠕嫁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锨天,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354