你們好,我是林三心,相信你們在平常開發中都用過Promise,我一直有個夢想,就是以最通俗的話,講最複雜的知識,因此我把通俗易懂放在了首位,今天就帶你們手寫實現如下Promise吧,相信你們一看就懂。面試
我們來看一段Promise的代碼:數組
let p1 = new Promise((resolve, reject) => {
resolve('成功')
reject('失敗')
})
console.log('p1', p1)
let p2 = new Promise((resolve, reject) => {
reject('失敗')
resolve('成功')
})
console.log('p2', p2)
let p3 = new Promise((resolve, reject) => {
throw('報錯')
})
console.log('p3', p3)
複製代碼
那麼會輸出什麼呢?請看:promise
這裏暴露出了四個知識點:markdown
resolve
,Promise狀態會變成fulfilled
reject
,Promise狀態會變成rejected
第一次爲準
,第一次成功就永久
爲fulfilled
,第一次失敗就永遠狀態爲rejected
throw
的話,就至關於執行了reject
那麼我們就把這四個知識點一步步實現吧!!!app
你們要注意:Promise的初始狀態是pending
異步
這裏很重要的一步是resolve和reject的綁定this
,爲何要綁定this
呢?這是爲了resolve和reject的this指向
永遠指向當前的MyPromise實例
,防止隨着函數執行環境的改變而改變ide
class MyPromise {
// 構造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
// 執行傳進來的函數
executor(this.resolve, this.reject)
}
initBind() {
// 初始化this
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
// 初始化值
this.PromiseResult = null // 終值
this.PromiseState = 'pending' // 狀態
}
resolve(value) {
// 若是執行resolve,狀態變爲fulfilled
this.PromiseState = 'fulfilled'
// 終值爲傳進來的值
this.PromiseResult = value
}
reject(reason) {
// 若是執行reject,狀態變爲rejected
this.PromiseState = 'rejected'
// 終值爲傳進來的reason
this.PromiseResult = reason
}
}
複製代碼
我們來測試一下代碼吧:函數
const test1 = new MyPromise((resolve, reject) => {
resolve('成功')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: '成功' }
const test2 = new MyPromise((resolve, reject) => {
reject('失敗')
})
console.log(test2) // MyPromise { PromiseState: 'rejected', PromiseResult: '失敗' }
複製代碼
其實上面的代碼是有問題的,什麼問題呢?看看:學習
const test1 = new MyPromise((resolve, reject) => {
resolve('成功')
reject('失敗')
})
console.log(test1) // MyPromise { PromiseState: 'rejected', PromiseResult: '失敗' }
複製代碼
正確的應該是狀態爲fulfilled
,結果是成功
,這裏明顯沒有以第一次爲準
測試
以前說了,Promise只以第一次爲準
,第一次成功就永久
爲fulfilled
,第一次失敗就永遠狀態爲rejected
,具體是什麼流程呢?我給你們畫了一張圖:
Promise有三種狀態:
pending
:等待中,是初始狀態fulfilled
:成功狀態rejected
:失敗狀態一旦狀態從pending
變爲fulfilled或者rejected
,那麼此Promise實例的狀態就定死了。
其實實現起來也很容易,加個判斷條件就行:
resolve(value) {
// state是不可變的
+ if (this.PromiseState !== 'pending') return
// 若是執行resolve,狀態變爲fulfilled
this.PromiseState = 'fulfilled'
// 終值爲傳進來的值
this.PromiseResult = value
}
reject(reason) {
// state是不可變的
+ if (this.PromiseState !== 'pending') return
// 若是執行reject,狀態變爲rejected
this.PromiseState = 'rejected'
// 終值爲傳進來的reason
this.PromiseResult = reason
}
複製代碼
再來看看效果:
const test1 = new MyPromise((resolve, reject) => {
// 只以第一次爲準
resolve('成功')
reject('失敗')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: '成功' }
複製代碼
Promise中有throw
的話,就至關於執行了reject
。這就要使用try catch
了
+ try {
// 執行傳進來的函數
executor(this.resolve, this.reject)
+ } catch (e) {
// 捕捉到錯誤直接執行reject
+ this.reject(e)
+ }
複製代碼
我們來看看效果:
const test3 = new MyPromise((resolve, reject) => {
throw('失敗')
})
console.log(test3) // MyPromise { PromiseState: 'rejected', PromiseResult: '失敗' }
複製代碼
我們平時使用then方法是這麼用的:
// 立刻輸出 」成功「
const p1 = new Promise((resolve, reject) => {
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
// 1秒後輸出 」失敗「
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失敗')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
// 鏈式調用 輸出 200
const p3 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err))
複製代碼
能夠總結出這幾個知識點:
成功回調
,一個是失敗回調
fulfilled
執行成功回調
,爲rejected
執行失敗回調
則定時器結束後再執行then
鏈式調用
,下一次then執行受上一次then返回值的影響
下面我們就一步一步地去實現他吧
then(onFulfilled, onRejected) {
// 接收兩個回調 onFulfilled, onRejected
// 參數校驗,確保必定是函數
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 若是當前爲成功狀態,執行第一個回調
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 若是當前爲失敗狀態,執行第二哥回調
onRejected(this.PromiseResult)
}
}
複製代碼
我們來看看效果:
// 輸出 」成功「
const test = new MyPromise((resolve, reject) => {
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
複製代碼
上面咱們已經實現了then
的基本功能。那若是是定時器
狀況呢?
仍是那個代碼,怎麼才能保證,1秒後才執行then裏的失敗回調呢?
// 1秒後輸出 」成功「
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失敗')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
複製代碼
咱們不能確保1秒後才執行then函數,可是咱們能夠保證1秒後再執行then裏的回調,可能這裏你們有點懵逼,我一樣用一張圖給你們講講吧:
也就是在這1秒時間內,咱們能夠先把then裏的兩個回調保存起來,而後等到1秒事後,執行了resolve或者reject,我們再去判斷狀態,而且判斷要去執行剛剛保存的兩個回調中的哪個回調。
那麼問題來了,咱們怎麼知道當前1秒還沒走完甚至還沒開始走呢?其實很好判斷,只要狀態是pending
,那就證實定時器還沒跑完,由於若是定時器跑完的話,那狀態確定就不是pending
,而是fulfilled或者rejected
那是用什麼來保存這些回調呢?建議使用數組
,由於一個promise實例可能會屢次then
,用數組就一個一個保存了
initValue() {
// 初始化值
this.PromiseResult = null // 終值
this.PromiseState = 'pending' // 狀態
+ this.onFulfilledCallbacks = [] // 保存成功回調
+ this.onRejectedCallbacks = [] // 保存失敗回調
}
resolve(value) {
// state是不可變的
if (this.PromiseState !== 'pending') return
// 若是執行resolve,狀態變爲fulfilled
this.PromiseState = 'fulfilled'
// 終值爲傳進來的值
this.PromiseResult = value
// 執行保存的成功回調
+ while (this.onFulfilledCallbacks.length) {
+ this.onFulfilledCallbacks.shift()(this.PromiseResult)
+ }
}
reject(reason) {
// state是不可變的
if (this.PromiseState !== 'pending') return
// 若是執行reject,狀態變爲rejected
this.PromiseState = 'rejected'
// 終值爲傳進來的reason
this.PromiseResult = reason
// 執行保存的失敗回調
+ while (this.onRejectedCallbacks.length) {
+ this.onRejectedCallbacks.shift()(this.PromiseResult)
+ }
}
then(onFulfilled, onRejected) {
// 接收兩個回調 onFulfilled, onRejected
// 參數校驗,確保必定是函數
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 若是當前爲成功狀態,執行第一個回調
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 若是當前爲失敗狀態,執行第二哥回調
onRejected(this.PromiseResult)
+ } else if (this.PromiseState === 'pending') {
+ // 若是狀態爲待定狀態,暫時保存兩個回調
+ this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+ this.onRejectedCallbacks.push(onRejected.bind(this))
+ }
}
複製代碼
加完上面的代碼,我們來看看定時器的效果吧:
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 1秒後輸出 成功
// resolve('成功') // 1秒後輸出 失敗
}, 1000)
}).then(res => console.log(res), err => console.log(err))
複製代碼
then支持鏈式調用
,下一次then執行受上一次then返回值的影響
,給你們舉個例子:
// 鏈式調用 輸出 200
const p3 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err))
// 鏈式調用 輸出300
const p4 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err))
.then(res => console.log(res), err => console.log(err))
複製代碼
從上方例子,咱們能夠獲取到幾個知識點:
我們知道then是Promise上的方法,那如何實現then完還能再then呢?很簡單,then執行後返回一個Promise對象
就好了,就能保證then完還能繼續執行then:
代碼實現:
then(onFulfilled, onRejected) {
// 接收兩個回調 onFulfilled, onRejected
// 參數校驗,確保必定是函數
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = cb => {
try {
const x = cb(this.PromiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 若是返回值是Promise
// 若是返回值是promise對象,返回值爲成功,新promise就是成功
// 若是返回值是promise對象,返回值爲失敗,新promise就是失敗
// 誰知道返回的promise是失敗成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 處理報錯
reject(err)
}
}
if (this.PromiseState === 'fulfilled') {
// 若是當前爲成功狀態,執行第一個回調
resolvePromise(onFulfilled)
} else if (this.PromiseState === 'rejected') {
// 若是當前爲失敗狀態,執行第二個回調
resolvePromise(onRejected)
} else if (this.PromiseState === 'pending') {
// 若是狀態爲待定狀態,暫時保存兩個回調
// 若是狀態爲待定狀態,暫時保存兩個回調
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
}
})
// 返回這個包裝的Promise
return thenPromise
}
複製代碼
如今你們能夠試試效果怎麼樣了,你們要邊敲邊試哦:
const test3 = new MyPromise((resolve, reject) => {
resolve(100) // 輸出 狀態:成功 值: 200
// reject(100) // 輸出 狀態:失敗 值:300
}).then(res => 2 * res, err => 3 * err)
.then(res => console.log(res), err => console.log(err))
const test4 = new MyPromise((resolve, reject) => {
resolve(100) // 輸出 狀態:失敗 值:200
// reject(100) // 輸出 狀態:成功 值:300
// 這裏可沒搞反哦。真的搞懂了,就知道了爲啥這裏是反的
}).then(res => new MyPromise((resolve, reject) => reject(2 * res)), err => new MyPromise((resolve, reject) => resolve(2 * res)))
.then(res => console.log(res), err => console.log(err))
複製代碼
看過js執行機制
的兄弟都知道,then方法是微任務
,啥叫微任務呢?其實不知道也沒關係,我經過下面例子讓你知道:
const p = new Promise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
輸出順序是 2 1
複製代碼
爲啥不是 1 2 呢?由於then是個微任務啊。。。一樣,咱們也要給咱們的MyPromise加上這個特性(我這裏使用定時器,你們別介意哈)
只須要讓resolvePromise函數
異步執行就能夠了
const resolvePromise = cb => {
setTimeout(() => {
try {
const x = cb(this.PromiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 若是返回值是Promise
// 若是返回值是promise對象,返回值爲成功,新promise就是成功
// 若是返回值是promise對象,返回值爲失敗,新promise就是失敗
// 誰知道返回的promise是失敗成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 處理報錯
reject(err)
}
})
}
複製代碼
看看效果:
const test4 = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
輸出順序 2 1
複製代碼
這些方法都比較簡單,我就不太過詳細地講了,你們也能夠借這個機會,本身摸索,鞏固這篇文章的知識。
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = (index, value) => {
result[index] = value
count++
if (count === promises.length) resolve(result)
}
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(index, res)
}, err => reject(err))
} else {
addData(index, promise)
}
})
})
}
複製代碼
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}
複製代碼
static allSettled(promises) {
return new Promise((resolve, reject) => {
const res = []
let count = 0
const addData = (status, value, i) => {
res[i] = {
status,
value
}
count++
if (count === promises.length) {
resolve(res)
}
}
promises.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData('fulfilled', res, i)
}, err => {
addData('rejected', err, i)
})
} else {
addData('fulfilled', promise, i)
}
})
})
}
複製代碼
any與all相反
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((promise) => {
promise.then(val => {
resolve(val)
}, err => {
count++
if (count === promises.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
}
複製代碼
不再怕面試官問你Promise原理啦哈哈哈哈😁
若是你以爲此文章對你有一丁點幫助的話,點個讚唄,謝謝你
學習羣請點這裏