成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript之手寫(xiě)Promise

stefan / 3237人閱讀

摘要:如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中函數(shù),比如如下代碼就會(huì)進(jìn)入等待態(tài)的邏輯以上就是簡(jiǎn)單版實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)符合規(guī)范的接下來(lái)大部分代碼都是根據(jù)規(guī)范去實(shí)現(xiàn)的。

為更好的理解, 推薦閱讀Promise/A+ 規(guī)范
實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise

在完成符合 Promise/A+ 規(guī)范的代碼之前,我們可以先來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise,因?yàn)樵诿嬖囍?,如果你能?shí)現(xiàn)出一個(gè)簡(jiǎn)易版的 Promise 基本可以過(guò)關(guān)了。

那么我們先來(lái)搭建構(gòu)建函數(shù)的大體框架

const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"

function MyPromise(fn) {
  const that = this
  that.state = PENDING
  that.value = null
  that.resolvedCallbacks = []
  that.rejectedCallbacks = []
  // 待完善 resolve 和 reject 函數(shù)
  // 待完善執(zhí)行 fn 函數(shù)
}

首先我們創(chuàng)建了三個(gè)常量用于表示狀態(tài),對(duì)于經(jīng)常使用的一些值都應(yīng)該通過(guò)常量來(lái)管理,便于開(kāi)發(fā)及后期維護(hù)

在函數(shù)體內(nèi)部首先創(chuàng)建了常量 that,因?yàn)榇a可能會(huì)異步執(zhí)行,用于獲取正確的 this 對(duì)象

一開(kāi)始 Promise 的狀態(tài)應(yīng)該是 pending

value 變量用于保存 resolve 或者 reject 中傳入的值

resolvedCallbacksrejectedCallbacks 用于保存 then 中的回調(diào),因?yàn)楫?dāng)執(zhí)行完 Promise 時(shí)狀態(tài)可能還是等待中,這時(shí)候應(yīng)該把 then 中的回調(diào)保存起來(lái)用于狀態(tài)改變時(shí)使用

接下來(lái)我們來(lái)完善 resolve 和 reject 函數(shù),添加在 MyPromise 函數(shù)體內(nèi)部

function resolve(value) {
  if (that.state === PENDING) {
    that.state = RESOLVED
    that.value = value
    that.resolvedCallbacks.map(cb => cb(that.value))
  }
}

function reject(value) {
  if (that.state === PENDING) {
    that.state = REJECTED
    that.value = value
    that.rejectedCallbacks.map(cb => cb(that.value))
  }
}

這兩個(gè)函數(shù)代碼類似,就一起解析了

首先兩個(gè)函數(shù)都得判斷當(dāng)前狀態(tài)是否為等待中,因?yàn)橐?guī)范規(guī)定只有等待態(tài)才可以改變狀態(tài)

將當(dāng)前狀態(tài)更改為對(duì)應(yīng)狀態(tài),并且將傳入的值賦值給 value

遍歷回調(diào)數(shù)組并執(zhí)行

完成以上兩個(gè)函數(shù)以后,我們就該實(shí)現(xiàn)如何執(zhí)行 Promise 中傳入的函數(shù)了

try {
  fn(resolve, reject)
} catch (e) {
  reject(e)
}

實(shí)現(xiàn)很簡(jiǎn)單,執(zhí)行傳入的參數(shù)并且將之前兩個(gè)函數(shù)當(dāng)做參數(shù)傳進(jìn)去

要注意的是,可能執(zhí)行函數(shù)過(guò)程中會(huì)遇到錯(cuò)誤,需要捕獲錯(cuò)誤并且執(zhí)行 reject 函數(shù)

最后我們來(lái)實(shí)現(xiàn)較為復(fù)雜的 then 函數(shù)

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : r => {
          throw r
        }
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled)
    that.rejectedCallbacks.push(onRejected)
  }
  if (that.state === RESOLVED) {
    onFulfilled(that.value)
  }
  if (that.state === REJECTED) {
    onRejected(that.value)
  }
}

首先判斷兩個(gè)參數(shù)是否為函數(shù)類型,因?yàn)檫@兩個(gè)參數(shù)是可選參數(shù)

當(dāng)參數(shù)不是函數(shù)類型時(shí),需要?jiǎng)?chuàng)建一個(gè)函數(shù)賦值給對(duì)應(yīng)的參數(shù),同時(shí)也實(shí)現(xiàn)了透?jìng)?,比如如下代碼

// 該代碼目前在簡(jiǎn)單版中會(huì)報(bào)錯(cuò)
// 只是作為一個(gè)透?jìng)鞯睦?Promise.resolve(4).then().then((value) => console.log(value))

接下來(lái)就是一系列判斷狀態(tài)的邏輯,當(dāng)狀態(tài)不是等待態(tài)時(shí),就去執(zhí)行相對(duì)應(yīng)的函數(shù)。如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中 push 函數(shù),比如如下代碼就會(huì)進(jìn)入等待態(tài)的邏輯

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  }, 0)
}).then(value => {
  console.log(value)
})

以上就是簡(jiǎn)單版 Promise 實(shí)現(xiàn)

實(shí)現(xiàn)一個(gè)符合 Promise/A+ 規(guī)范的 Promise
接下來(lái)大部分代碼都是根據(jù)規(guī)范去實(shí)現(xiàn)的。

我們先來(lái)改造一下 resolvereject 函數(shù)

function resolve(value) {
  if (value instanceof MyPromise) {
    return value.then(resolve, reject)
  }
  setTimeout(() => {
    if (that.state === PENDING) {
      that.state = RESOLVED
      that.value = value
      that.resolvedCallbacks.map(cb => cb(that.value))
    }
  }, 0)
}
function reject(value) {
  setTimeout(() => {
    if (that.state === PENDING) {
      that.state = REJECTED
      that.value = value
      that.rejectedCallbacks.map(cb => cb(that.value))
    }
  }, 0)
}

對(duì)于 resolve 函數(shù)來(lái)說(shuō),首先需要判斷傳入的值是否為 Promise 類型

為了保證函數(shù)執(zhí)行順序,需要將兩個(gè)函數(shù)體代碼使用 setTimeout 包裹起來(lái)

接下來(lái)繼續(xù)改造 then 函數(shù)中的代碼,首先我們需要新增一個(gè)變量 promise2,因?yàn)槊總€(gè) then 函數(shù)都需要返回一個(gè)新的 Promise 對(duì)象,該變量用于保存新的返回對(duì)象,然后我們先來(lái)改造判斷等待態(tài)的邏輯

if (that.state === PENDING) {
  return (promise2 = new MyPromise((resolve, reject) => {
    that.resolvedCallbacks.push(() => {
      try {
        const x = onFulfilled(that.value)
        resolutionProcedure(promise2, x, resolve, reject)
      } catch (r) {
        reject(r)
      }
    })

    that.rejectedCallbacks.push(() => {
      try {
        const x = onRejected(that.value)
        resolutionProcedure(promise2, x, resolve, reject)
      } catch (r) {
        reject(r)
      }
    })
  }))
}

首先我們返回了一個(gè)新的 Promise 對(duì)象,并在 Promise 中傳入了一個(gè)函數(shù)

函數(shù)的基本邏輯還是和之前一樣,往回調(diào)數(shù)組中 push 函數(shù)

同樣,在執(zhí)行函數(shù)的過(guò)程中可能會(huì)遇到錯(cuò)誤,所以使用了 try...catch 包裹

規(guī)范規(guī)定,執(zhí)行 onFulfilled 或者 onRejected 函數(shù)時(shí)會(huì)返回一個(gè) x,并且執(zhí)行 Promise 解決過(guò)程,這是為了不同的 Promise 都可以兼容使用,比如 JQueryPromise 能兼容 ES6Promise

接下來(lái)我們改造判斷執(zhí)行態(tài)的邏輯

if (that.state === RESOLVED) {
  return (promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      try {
        const x = onFulfilled(that.value)
        resolutionProcedure(promise2, x, resolve, reject)
      } catch (reason) {
        reject(reason)
      }
    })
  }))
}

其實(shí)大家可以發(fā)現(xiàn)這段代碼和判斷等待態(tài)的邏輯基本一致,無(wú)非是傳入的函數(shù)的函數(shù)體需要異步執(zhí)行,這也是規(guī)范規(guī)定的

對(duì)于判斷拒絕態(tài)的邏輯這里就不一一贅述了,留給大家自己完成這個(gè)作業(yè)

最后,當(dāng)然也是最難的一部分,也就是實(shí)現(xiàn)兼容多種 PromiseresolutionProcedure 函數(shù)

function resolutionProcedure(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("Error"))
  }
}

首先規(guī)范規(guī)定了 x 不能與 promise2 相等,這樣會(huì)發(fā)生循環(huán)引用的問(wèn)題,比如如下代碼

let p = new MyPromise((resolve, reject) => {
  resolve(1)
})
let p1 = p.then(value => {
  return p1
})

然后需要判斷 x 的類型

if (x instanceof MyPromise) {
    x.then(function(value) {
        resolutionProcedure(promise2, value, resolve, reject)
    }, reject)
}

這里的代碼是完全按照規(guī)范實(shí)現(xiàn)的。如果 xPromise 的話,需要判斷以下幾個(gè)情況:

如果 x 處于等待態(tài),Promise 需保持為等待態(tài)直至 x 被執(zhí)行或拒絕

如果 x 處于其他狀態(tài),則用相同的值處理 Promise

當(dāng)然以上這些是規(guī)范需要我們判斷的情況,實(shí)際上我們不判斷狀態(tài)也是可行的。

接下來(lái)我們繼續(xù)按照規(guī)范來(lái)實(shí)現(xiàn)剩余的代碼

let called = false
if (x !== null && (typeof x === "object" || typeof x === "function")) {
  try {
    let then = x.then
    if (typeof then === "function") {
      then.call(
        x,
        y => {
          if (called) return
          called = true
          resolutionProcedure(promise2, y, resolve, reject)
        },
        e => {
          if (called) return
          called = true
          reject(e)
        }
      )
    } else {
      resolve(x)
    }
  } catch (e) {
    if (called) return
    called = true
    reject(e)
  }
} else {
  resolve(x)
}

首先創(chuàng)建一個(gè)變量 called 用于判斷是否已經(jīng)調(diào)用過(guò)函數(shù)

然后判斷 x 是否為對(duì)象或者函數(shù),如果都不是的話,將 x 傳入 resolve

如果 x 是對(duì)象或者函數(shù)的話,先把 x.then 賦值給 then,然后判斷 then 的類型,如果不是函數(shù)類型的話,就將 x 傳入 resolve

如果 then 是函數(shù)類型的話,就將 x 作為函數(shù)的作用域 this 調(diào)用之,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù),第一個(gè)參數(shù)叫做 resolvePromise ,第二個(gè)參數(shù)叫做 rejectPromise,兩個(gè)回調(diào)函數(shù)都需要判斷是否已經(jīng)執(zhí)行過(guò)函數(shù),然后進(jìn)行相應(yīng)的邏輯

以上代碼在執(zhí)行的過(guò)程中如果拋錯(cuò)了,將錯(cuò)誤傳入 reject 函數(shù)中

以上就是符合 Promise/A+ 規(guī)范的實(shí)現(xiàn)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102016.html

相關(guān)文章

  • 前端從零開(kāi)始系列

    摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門(mén)技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫(xiě)函數(shù)庫(kù)中高級(jí)前端面試手寫(xiě)代碼無(wú)敵秘籍如何用不到行代碼寫(xiě)一款屬于自己的類庫(kù)原理講解實(shí)現(xiàn)一個(gè)對(duì)象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門(mén)技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...

    Youngdze 評(píng)論0 收藏0
  • 「中高級(jí)前端面試」JavaScript手寫(xiě)代碼無(wú)敵秘籍

    摘要:第一種直接調(diào)用避免在不必要的情況下使用,是一個(gè)危險(xiǎn)的函數(shù),他執(zhí)行的代碼擁有著執(zhí)行者的權(quán)利。來(lái)自于此外,實(shí)現(xiàn)需要考慮實(shí)例化后對(duì)原型鏈的影響。函數(shù)柯里化的主要作用和特點(diǎn)就是參數(shù)復(fù)用提前返回和延遲執(zhí)行。手寫(xiě)路徑導(dǎo)航 實(shí)現(xiàn)一個(gè)new操作符 實(shí)現(xiàn)一個(gè)JSON.stringify 實(shí)現(xiàn)一個(gè)JSON.parse 實(shí)現(xiàn)一個(gè)call或 apply 實(shí)現(xiàn)一個(gè)Function.bind 實(shí)現(xiàn)一個(gè)繼承 實(shí)現(xiàn)一個(gè)J...

    Zhuxy 評(píng)論0 收藏0
  • 22道JavaScript高頻手寫(xiě)面試題

    JavaScript筆試部分 點(diǎn)擊關(guān)注本公眾號(hào)獲取文檔最新更新,并可以領(lǐng)取配套于本指南的 《前端面試手冊(cè)》 以及最標(biāo)準(zhǔn)的簡(jiǎn)歷模板. 實(shí)現(xiàn)防抖函數(shù)(debounce) 防抖函數(shù)原理:在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計(jì)時(shí)。 那么與節(jié)流函數(shù)的區(qū)別直接看這個(gè)動(dòng)畫(huà)實(shí)現(xiàn)即可。 showImg(https://segmentfault.com/img/remote/146000002...

    Alan 評(píng)論0 收藏0
  • 2019-我的前端面試題

    摘要:先說(shuō)下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問(wèn)到相關(guān)問(wèn)題,其他公司壓根沒(méi)問(wèn)。我自己回答的是自己開(kāi)發(fā)組件面臨的問(wèn)題。完全不用擔(dān)心對(duì)方到時(shí)候打電話核對(duì)的問(wèn)題。 2019的5月9號(hào),離發(fā)工資還有1天的時(shí)候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說(shuō):阿郭,我們公司要倒閉了,錢(qián)是沒(méi)有的啦,為了不耽誤你,你趕緊出去找工作吧。聽(tīng)到這話,我虎軀一震,這已經(jīng)是第2個(gè)月沒(méi)工資了。 公...

    iKcamp 評(píng)論0 收藏0
  • What's New in JavaScript

    摘要:在和中都保留了數(shù)組的強(qiáng)引用,所以在中簡(jiǎn)單的清除變量?jī)?nèi)存并沒(méi)有得到釋放,因?yàn)檫€存在引用計(jì)數(shù)。而在中,它的鍵是弱引用,不計(jì)入引用計(jì)數(shù)中,所以當(dāng)被清除之后,數(shù)組會(huì)因?yàn)橐糜?jì)數(shù)為而被回收掉。其實(shí)我們主要注意的引用是不計(jì)引用計(jì)數(shù)的,就好理解了。 showImg(https://segmentfault.com/img/remote/1460000019147368?w=900&h=383); 前...

    cgh1999520 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<