摘要:如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中函數(shù),比如如下代碼就會進入等待態(tài)的邏輯以上就是簡單版實現(xiàn)實現(xiàn)一個符合規(guī)范的接下來大部分代碼都是根據(jù)規(guī)范去實現(xiàn)的。
為更好的理解, 推薦閱讀Promise/A+ 規(guī)范實現(xiàn)一個簡易版 Promise
在完成符合 Promise/A+ 規(guī)范的代碼之前,我們可以先來實現(xiàn)一個簡易版 Promise,因為在面試中,如果你能實現(xiàn)出一個簡易版的 Promise 基本可以過關(guān)了。
那么我們先來搭建構(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)建了三個常量用于表示狀態(tài),對于經(jīng)常使用的一些值都應(yīng)該通過常量來管理,便于開發(fā)及后期維護
在函數(shù)體內(nèi)部首先創(chuàng)建了常量 that,因為代碼可能會異步執(zhí)行,用于獲取正確的 this 對象
一開始 Promise 的狀態(tài)應(yīng)該是 pending
value 變量用于保存 resolve 或者 reject 中傳入的值
resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回調(diào),因為當(dāng)執(zhí)行完 Promise 時狀態(tài)可能還是等待中,這時候應(yīng)該把 then 中的回調(diào)保存起來用于狀態(tà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)) } }
這兩個函數(shù)代碼類似,就一起解析了
首先兩個函數(shù)都得判斷當(dāng)前狀態(tài)是否為等待中,因為規(guī)范規(guī)定只有等待態(tài)才可以改變狀態(tài)
將當(dāng)前狀態(tài)更改為對應(yīng)狀態(tài),并且將傳入的值賦值給 value
遍歷回調(diào)數(shù)組并執(zhí)行
完成以上兩個函數(shù)以后,我們就該實現(xiàn)如何執(zhí)行 Promise 中傳入的函數(shù)了
try { fn(resolve, reject) } catch (e) { reject(e) }
實現(xiàn)很簡單,執(zhí)行傳入的參數(shù)并且將之前兩個函數(shù)當(dāng)做參數(shù)傳進去
要注意的是,可能執(zhí)行函數(shù)過程中會遇到錯誤,需要捕獲錯誤并且執(zhí)行 reject 函數(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) } }
首先判斷兩個參數(shù)是否為函數(shù)類型,因為這兩個參數(shù)是可選參數(shù)
當(dāng)參數(shù)不是函數(shù)類型時,需要創(chuàng)建一個函數(shù)賦值給對應(yīng)的參數(shù),同時也實現(xiàn)了透傳,比如如下代碼
// 該代碼目前在簡單版中會報錯 // 只是作為一個透傳的例子 Promise.resolve(4).then().then((value) => console.log(value))
接下來就是一系列判斷狀態(tài)的邏輯,當(dāng)狀態(tài)不是等待態(tài)時,就去執(zhí)行相對應(yīng)的函數(shù)。如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中 push 函數(shù),比如如下代碼就會進入等待態(tài)的邏輯
new MyPromise((resolve, reject) => { setTimeout(() => { resolve(1) }, 0) }).then(value => { console.log(value) })
以上就是簡單版 Promise 實現(xiàn)
實現(xiàn)一個符合 Promise/A+ 規(guī)范的 Promise接下來大部分代碼都是根據(jù)規(guī)范去實現(xiàn)的。
我們先來改造一下 resolve 和 reject 函數(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) }
對于 resolve 函數(shù)來說,首先需要判斷傳入的值是否為 Promise 類型
為了保證函數(shù)執(zhí)行順序,需要將兩個函數(shù)體代碼使用 setTimeout 包裹起來
接下來繼續(xù)改造 then 函數(shù)中的代碼,首先我們需要新增一個變量 promise2,因為每個 then 函數(shù)都需要返回一個新的 Promise 對象,該變量用于保存新的返回對象,然后我們先來改造判斷等待態(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) } }) })) }
首先我們返回了一個新的 Promise 對象,并在 Promise 中傳入了一個函數(shù)
函數(shù)的基本邏輯還是和之前一樣,往回調(diào)數(shù)組中 push 函數(shù)
同樣,在執(zhí)行函數(shù)的過程中可能會遇到錯誤,所以使用了 try...catch 包裹
規(guī)范規(guī)定,執(zhí)行 onFulfilled 或者 onRejected 函數(shù)時會返回一個 x,并且執(zhí)行 Promise 解決過程,這是為了不同的 Promise 都可以兼容使用,比如 JQuery 的 Promise 能兼容 ES6 的 Promise
接下來我們改造判斷執(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) } }) })) }
其實大家可以發(fā)現(xiàn)這段代碼和判斷等待態(tài)的邏輯基本一致,無非是傳入的函數(shù)的函數(shù)體需要異步執(zhí)行,這也是規(guī)范規(guī)定的
對于判斷拒絕態(tài)的邏輯這里就不一一贅述了,留給大家自己完成這個作業(yè)
最后,當(dāng)然也是最難的一部分,也就是實現(xiàn)兼容多種 Promise 的 resolutionProcedure 函數(shù)
function resolutionProcedure(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError("Error")) } }
首先規(guī)范規(guī)定了 x 不能與 promise2 相等,這樣會發(fā)生循環(huá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ī)范實現(xiàn)的。如果 x 為 Promise 的話,需要判斷以下幾個情況:
如果 x 處于等待態(tài),Promise 需保持為等待態(tài)直至 x 被執(zhí)行或拒絕
如果 x 處于其他狀態(tài),則用相同的值處理 Promise
當(dāng)然以上這些是規(guī)范需要我們判斷的情況,實際上我們不判斷狀態(tài)也是可行的。
接下來我們繼續(xù)按照規(guī)范來實現(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)建一個變量 called 用于判斷是否已經(jīng)調(diào)用過函數(shù)
然后判斷 x 是否為對象或者函數(shù),如果都不是的話,將 x 傳入 resolve 中
如果 x 是對象或者函數(shù)的話,先把 x.then 賦值給 then,然后判斷 then 的類型,如果不是函數(shù)類型的話,就將 x 傳入 resolve 中
如果 then 是函數(shù)類型的話,就將 x 作為函數(shù)的作用域 this 調(diào)用之,并且傳遞兩個回調(diào)函數(shù)作為參數(shù),第一個參數(shù)叫做 resolvePromise ,第二個參數(shù)叫做 rejectPromise,兩個回調(diào)函數(shù)都需要判斷是否已經(jīng)執(zhí)行過函數(shù),然后進行相應(yīng)的邏輯
以上代碼在執(zhí)行的過程中如果拋錯了,將錯誤傳入 reject 函數(shù)中
以上就是符合 Promise/A+ 規(guī)范的實現(xiàn)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/102016.html
摘要:第一種直接調(diào)用避免在不必要的情況下使用,是一個危險的函數(shù),他執(zhí)行的代碼擁有著執(zhí)行者的權(quán)利。來自于此外,實現(xiàn)需要考慮實例化后對原型鏈的影響。函數(shù)柯里化的主要作用和特點就是參數(shù)復(fù)用提前返回和延遲執(zhí)行。手寫路徑導(dǎo)航 實現(xiàn)一個new操作符 實現(xiàn)一個JSON.stringify 實現(xiàn)一個JSON.parse 實現(xiàn)一個call或 apply 實現(xiàn)一個Function.bind 實現(xiàn)一個繼承 實現(xiàn)一個J...
JavaScript筆試部分 點擊關(guān)注本公眾號獲取文檔最新更新,并可以領(lǐng)取配套于本指南的 《前端面試手冊》 以及最標(biāo)準(zhǔn)的簡歷模板. 實現(xiàn)防抖函數(shù)(debounce) 防抖函數(shù)原理:在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。 那么與節(jié)流函數(shù)的區(qū)別直接看這個動畫實現(xiàn)即可。 showImg(https://segmentfault.com/img/remote/146000002...
摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關(guān)問題,其他公司壓根沒問。我自己回答的是自己開發(fā)組件面臨的問題。完全不用擔(dān)心對方到時候打電話核對的問題。 2019的5月9號,離發(fā)工資還有1天的時候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經(jīng)是第2個月沒工資了。 公...
摘要:在和中都保留了數(shù)組的強引用,所以在中簡單的清除變量內(nèi)存并沒有得到釋放,因為還存在引用計數(shù)。而在中,它的鍵是弱引用,不計入引用計數(shù)中,所以當(dāng)被清除之后,數(shù)組會因為引用計數(shù)為而被回收掉。其實我們主要注意的引用是不計引用計數(shù)的,就好理解了。 showImg(https://segmentfault.com/img/remote/1460000019147368?w=900&h=383); 前...
閱讀 1915·2021-11-25 09:43
閱讀 1421·2021-11-22 15:08
閱讀 3869·2021-11-22 09:34
閱讀 3289·2021-09-04 16:40
閱讀 3393·2021-09-04 16:40
閱讀 604·2019-08-30 15:54
閱讀 1397·2019-08-29 17:19
閱讀 1816·2019-08-28 18:13