深刻理解Promise系列(五):一個(gè)更好的Promise實(shí)現(xiàn)

function MyPromise(fn) {
  this.resData // 最終resolve時(shí)的value 或者 最終reject時(shí)的reason
  this.onFulfilledList = [] // 當(dāng)resolve之后奸汇,執(zhí)行的內(nèi)部回調(diào)隊(duì)列
  this.onRejectedList = [] // 當(dāng)reject之后晓殊,執(zhí)行的內(nèi)部回調(diào)隊(duì)列
  this.status = 'pending' // 內(nèi)部的狀態(tài)矮固,只能是pending、fulfilled呆瞻、rejected中的一種,且只能由pending變化為其他兩種,且一旦狀態(tài)改變過柴我,就無(wú)法再進(jìn)行改變

  // 第三步:執(zhí)行resolve
  // 注:極少數(shù)情況,resolve在then之前調(diào)用扩然,相當(dāng)于Promise對(duì)象狀態(tài)已經(jīng)確定后再去執(zhí)行then注冊(cè)的回調(diào)
  const resolve = data => {
    // 只有在pending狀態(tài)下才會(huì)繼續(xù)艘儒,保證resolve狀態(tài)不會(huì)被改變
    if (this.status != 'pending') return
    // 同步代碼,立即修改狀態(tài)為fulfilled,賦值resData界睁,執(zhí)行回調(diào)
    this.status = 'fulfilled'
    this.resData = data
    // 若執(zhí)行此回調(diào)時(shí)觉增,onFulfilledList為空,則相當(dāng)于無(wú)事發(fā)生
    if (this.onFulfilledList.length > 0) {
      for (let fn of this.onFulfilledList) {
        fn(data)
      }
    }
  }

  // 第三步:執(zhí)行reject
  // 注:極少數(shù)情況翻斟,reject在then之前調(diào)用逾礁,相當(dāng)于Promise對(duì)象狀態(tài)已經(jīng)確定后再去執(zhí)行then注冊(cè)的回調(diào)
  const reject = reason => {
    // 只有在pending狀態(tài)下才會(huì)繼續(xù),保證reject狀態(tài)不會(huì)被改變
    if (this.status != 'pending') return
    // 同步代碼访惜,立即修改狀態(tài)為rejected嘹履,賦值resData,執(zhí)行回調(diào)
    this.status = 'rejected'
    this.resData = reason
    // 若執(zhí)行此回調(diào)時(shí)债热,onRejectedList為空植捎,則相當(dāng)于無(wú)事發(fā)生
    if (this.onRejectedList.length > 0) {
      for (let fn of this.onRejectedList) {
        fn(reason)
      }
    }
  }

  // 第一步執(zhí)行fn,這里給使用者提供一個(gè)權(quán)力:何時(shí)來resolve或reject
  try { // try catch 是為了捕獲錯(cuò)誤
    fn(resolve, reject)
  } catch (reason) {
    reject(reason) // 若捕獲到錯(cuò)誤阳柔,則直接reject
  }

}

// 第二步:調(diào)用then來注冊(cè)回調(diào)焰枢,以備原來的Promise對(duì)象的狀態(tài)發(fā)生改變時(shí),執(zhí)行相應(yīng)的回調(diào)方法
// 注:這是最核心的部分
MyPromise.prototype.then = function (onfulfilled, onrejected) {
  const self = this
  // onfulfilled 和 onrejected 分別對(duì)應(yīng) fulfilled 和 rejected 兩種狀態(tài)的回調(diào)方法
  // 參數(shù)若不存在舌剂,則給予默認(rèn)值济锄,此默認(rèn)值用于實(shí)現(xiàn)Promise的穿透效果
  if (typeof onfulfilled !== 'function') onfulfilled = value => value
  if (typeof onrejected !== 'function') onrejected = reason => { throw reason }

  // 若注冊(cè)回調(diào)時(shí),原來的Promise對(duì)象處于pending狀態(tài)
  // 我們把原來的Promise對(duì)象稱為oldP
  // 注:這是最復(fù)雜的部分
  if (this.status == 'pending') {
    // 則return一個(gè)新的Promise對(duì)象霍转,我們稱它為newP
    return new MyPromise((resolve, reject) => {
      // newP最終會(huì)resolve還是reject荐绝,取決于then注冊(cè)的回調(diào)執(zhí)行后的結(jié)果

      // 此時(shí)創(chuàng)建一個(gè)回調(diào)函數(shù),主要目標(biāo)是:當(dāng)oldP完成resolve的時(shí)候避消,執(zhí)行此回調(diào)低滩,并且根據(jù)執(zhí)行結(jié)果改變newP的狀態(tài)
      let newOnFulfilled = function (data) {
        let res
        try { // try catch 是為了捕獲錯(cuò)誤
          // 此處執(zhí)行的then注冊(cè)的回調(diào)
          res = onfulfilled(data)
        } catch (e) {
          reject(e) // 若捕獲到錯(cuò)誤,則直接reject
          return
        }
        // 若回調(diào)執(zhí)行的結(jié)果為Promise類型岩喷,我們稱它為3P
        if (res instanceof MyPromise) {
          // 則3P的狀態(tài)將決定newP的狀態(tài)恕沫,此處實(shí)現(xiàn)了Promise之間的串行等待
          // 3P達(dá)到fulfilled狀態(tài)的時(shí)候,會(huì)調(diào)用newP的resolve
          // 3P達(dá)到rejected狀態(tài)的時(shí)候纱意,會(huì)調(diào)用newP的reject
          res.then(resolve, reject)
        } else {
          // 若回調(diào)執(zhí)行結(jié)果不為Promise類型婶溯,則以此值作為value,令newP達(dá)到resolve
          resolve(res)
        }
      }
      // 將此回調(diào)函數(shù)放入外層Promise的onFulfilledList
      self.onFulfilledList.push(newOnFulfilled)

      // 大體與上面部分相同
      let newOnRejected = function (data) {
        let res
        try {
          res = onrejected(data)
        } catch (e) {
          reject(e)
          return
        }

        if (res instanceof MyPromise) {
          res.then(resolve, reject)
        } else {
          // 注意此處仍然是newP達(dá)到resolve偷霉,因?yàn)閛ldP達(dá)到reject并不影響新的Promise的狀態(tài)迄委,只有then注冊(cè)的回調(diào)的結(jié)果,才可以影響
          resolve(res)
        }
      }

      // 將此回調(diào)函數(shù)放入外層Promise的onRejectedList
      self.onRejectedList.push(newOnRejected)
    })


    // 若注冊(cè)回調(diào)時(shí)类少,Promise對(duì)象處于fulfilled狀態(tài)
  } else if (this.status == 'fulfilled') {
    // 直接執(zhí)行回調(diào)方法即可
    let res = onfulfilled(this.resData)
    // 若回調(diào)方法的結(jié)果為Promise對(duì)象叙身,則直接return,即為執(zhí)行then之后得到的新的Promise對(duì)象
    // or
    // 若回調(diào)方法的結(jié)果不為Promise對(duì)象硫狞,則作為新的Promise對(duì)象的value信轿,且默認(rèn)新的Promise對(duì)象狀態(tài)為fulfilled
    return MyPromise.resolve(res)

    // 若注冊(cè)回調(diào)時(shí)赞警,Promise對(duì)象處于rejected狀態(tài),大體與上面部分相同
  } else if (this.status == 'rejected') {
    // 直接執(zhí)行回調(diào)方法即可
    let res = onrejected(this.resData)
    // 若回調(diào)方法的結(jié)果為Promise對(duì)象虏两,則直接return愧旦,即為執(zhí)行then之后得到的新的Promise對(duì)象
    // or
    // 若回調(diào)方法的結(jié)果不為Promise對(duì)象,則作為新的Promise對(duì)象的value定罢,且新的Promise對(duì)象狀態(tài)仍然為fulfilled
    return MyPromise.resolve(res)
  }
}

// catch只是一個(gè)語(yǔ)法糖笤虫,完全可以用then替代
MyPromise.prototype.catch = function (onrejected) {
  return this.then(null, onrejected)
}

// 相當(dāng)于Promise.resolve方法,生成一個(gè)狀態(tài)為fulfilled的Promise對(duì)象
MyPromise.resolve = function (data) {
  if (data instanceof MyPromise) {
    return data
  } else {
    return new MyPromise(resolve => {
      resolve(data)
    })
  }
}

// 相當(dāng)于Promise.reject方法祖凫,生成一個(gè)狀態(tài)為rejected的Promise對(duì)象
MyPromise.reject = function (data) {
  return new MyPromise((undefined, reject) => {
    reject(data)
  })
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琼蚯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惠况,更是在濱河造成了極大的恐慌遭庶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稠屠,死亡現(xiàn)場(chǎng)離奇詭異峦睡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)权埠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門榨了,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人攘蔽,你說我怎么就攤上這事龙屉。” “怎么了满俗?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵转捕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我唆垃,道長(zhǎng)五芝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任降盹,我火速辦了婚禮与柑,結(jié)果婚禮上谤辜,老公的妹妹穿的比我還像新娘蓄坏。我一直安慰自己,他們只是感情好丑念,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布涡戳。 她就那樣靜靜地躺著,像睡著了一般脯倚。 火紅的嫁衣襯著肌膚如雪渔彰。 梳的紋絲不亂的頭發(fā)上嵌屎,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音恍涂,去河邊找鬼宝惰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛再沧,可吹牛的內(nèi)容都是我干的尼夺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼炒瘸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼淤堵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起顷扩,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拐邪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后隘截,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扎阶,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年婶芭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乘陪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雕擂,死狀恐怖啡邑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情井赌,我是刑警寧澤谤逼,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站仇穗,受9級(jí)特大地震影響流部,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纹坐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一枝冀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耘子,春花似錦果漾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捍歪,卻和暖如春户辱,著一層夾襖步出監(jiān)牢的瞬間鸵钝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工庐镐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恩商,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓必逆,卻偏偏與公主長(zhǎng)得像痕届,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子末患,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容