手寫promise

一個實現(xiàn)了Promise A+規(guī)范的promise
采用promises-aplus-tests包測試通過
測試過程中報過很多錯,本來以為是對于規(guī)范的實現(xiàn)有問題焰檩,后來發(fā)現(xiàn)是代碼在運行過程中報錯導致測試通不過
之后用vscode調(diào)試了代碼后就很容易發(fā)現(xiàn)問題憔涉,所以推薦把promises-aplus-tests安裝在項目下订框,方便調(diào)試

問題如下
  1. 其中一個問題是x.constructor === promise.constructor判斷時析苫,沒有考慮xundefined時會報錯
  2. 之前把resolvereject方法定義在constructor下時,在then方法里創(chuàng)建新promise實例,然后需要根據(jù)不同情況來改變子promise狀態(tài)衩侥。此時想要把這塊邏輯封裝起來時會發(fā)現(xiàn)国旷,子promise是拿不到的,因為還未完成變量賦值茫死,除非用setTimeout把代碼改成異步
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const PROMISE_ID = Math.random().toString(36).substring(2)

function isObjectOrFunction(x) {
  let type = typeof x
  return x !== null && (type === 'object' || type === 'function')
}

function isFunction(x) {
  return typeof x === 'function'
}

let id = 0
function nextId() {
  return id++
}

const resolvePromise = (promise, x) => {
  if (promise === x) {
    return reject(promise, new TypeError('Chaining cycle'))
  }
  if (x && x.constructor === promise.constructor) {
    // 兩種寫法 x instanceof MyPromise 和 x.constructor === promise.constructor跪但;需要注意的是:
    // 后一種必須判斷x是否存在 否則會報constructor of undefined的錯誤
    x.then((v) => {
      resolvePromise(promise, v)
    }, (e) => {
      reject(promise, e)
    })
  } else if (isObjectOrFunction(x)) {
    // 函數(shù)或?qū)ο?    let used = false
    let then = void 0
    try {
      then = x.then
    } catch (e) {
      return reject(promise, e)
    }
    if (isFunction(then)) {
      try {
        then.call(x, (v) => {
          if (used) return
          used = true
          resolvePromise(promise, v)
        }, (r) => {
          if (used) return
          used = true
          reject(promise, r)
        })
      } catch (e) {
        if (used) return
        used = true
        reject(promise, e)
      }
    } else {
      resolve(promise, x)
    }
  } else {
    // 普通值
    resolve(promise, x)
  }
}

function noop() {}

function flushSchedule(func) {
  setTimeout(func, 0)
}

function settlePromise(promise, callback, arg) {
  return () => {
    let x = void 0
    try {
      x = callback(arg)
    } catch (e) {
      return reject(promise, e)
    }
    resolvePromise(promise, x)
  }
}

function resolve (promise, value) {
  if (promise.state === PENDING) {
    promise.state = FULFILLED
    promise.value = value
    promise.onFulfilledCallbacks.forEach(fn => fn())
  }
}

function reject (promise, reason) {
  if (promise.state === PENDING) {
    promise.state = REJECTED
    promise.reason = reason
    promise.onRejectedCallbacks.forEach(fn => fn())
  }
}

class MyPromise {
  state = PENDING
  value = undefined
  reason = undefined
  onFulfilledCallbacks = []
  onRejectedCallbacks = []
  constructor(executor) {
    this[PROMISE_ID] = nextId()
    try {
      executor((value) => {
        resolve(this, value);
      }, (reason) => {
        reject(this, reason)
      })
    } catch (e) {
      reject(this, e)
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
    let child = new this.constructor(noop)

    if (this.state === FULFILLED) {
      flushSchedule(settlePromise(child, onFulfilled, this.value))
    } else if (this.state === REJECTED) {
      flushSchedule(settlePromise(child, onRejected, this.reason))
    } else if (this.state === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        flushSchedule(settlePromise(child, onFulfilled, this.value))
      })
      this.onRejectedCallbacks.push(() => {
        flushSchedule(settlePromise(child, onRejected, this.reason))
      })
    }

    return child
  }
}

// promises-aplus-tests測試MyPromise的測試代碼
MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {}
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

module.exports = MyPromise
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市峦萎,隨后出現(xiàn)的幾起案子屡久,更是在濱河造成了極大的恐慌,老刑警劉巖爱榔,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件被环,死亡現(xiàn)場離奇詭異,居然都是意外死亡详幽,警方通過查閱死者的電腦和手機筛欢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唇聘,“玉大人版姑,你說我怎么就攤上這事〕倮桑” “怎么了剥险?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宪肖。 經(jīng)常有香客問我炒嘲,道長,這世上最難降的妖魔是什么匈庭? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任夫凸,我火速辦了婚禮,結(jié)果婚禮上阱持,老公的妹妹穿的比我還像新娘夭拌。我一直安慰自己,他們只是感情好衷咽,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布鸽扁。 她就那樣靜靜地躺著,像睡著了一般镶骗。 火紅的嫁衣襯著肌膚如雪桶现。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天鼎姊,我揣著相機與錄音骡和,去河邊找鬼相赁。 笑死,一個胖子當著我的面吹牛慰于,可吹牛的內(nèi)容都是我干的钮科。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婆赠,長吁一口氣:“原來是場噩夢啊……” “哼绵脯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起休里,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蛆挫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后妙黍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璃吧,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年废境,在試婚紗的時候發(fā)現(xiàn)自己被綠了畜挨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡噩凹,死狀恐怖巴元,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驮宴,我是刑警寧澤逮刨,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站堵泽,受9級特大地震影響修己,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜迎罗,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一睬愤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纹安,春花似錦尤辱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至塔粒,卻和暖如春结借,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卒茬。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工船老, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咖熟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓努隙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辜昵。 傳聞我的和親對象是個殘疾皇子荸镊,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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