JavaScript面試必備技巧之手寫一個Promise
很多同學在面試的時候都會被要求手寫一個Promise,那么今天我總結了一些手寫Promise的方法,可以跟著我的思路一起來實現(xiàn)一個Promise,讓我們的面試更有把握。同時我們也會實現(xiàn)一下Promsie常見的方法比如:all、race、allSettled、any。
基本實現(xiàn)
首先我們可以用類來實現(xiàn)Promise,而且Promise有三種狀態(tài):pending、fulfilled、rejected。初始狀態(tài)為pending。還需要對Promise的終值進行初始化。Promise還有兩個方法resolve和reject。
Promise有四個特點:
- 執(zhí)行了resolve,Promise狀態(tài)就會變成fulfilled
- 執(zhí)行了reject,Promise狀態(tài)就會變成rejected
- Promise狀態(tài)不可逆,第一次成功就永久為fulfilled,第一次失敗就永久為rejected
- Promise中有throw的話,就相當于執(zhí)行了rejected
下面我就來簡單的實現(xiàn)一下吧
實現(xiàn)resolve和reject
class MyPromise {
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
initValue() {
// 初始化值
this.promiseResult = null
this.promiseState = "pending" // 初始狀態(tài)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(val) {
this.promiseState = "fulfilled"
this.promiseResult = val
}
reject(reason) {
this.promiseState = "rejected"
this.promiseResult = reason
}
}測試一下吧
const test1 = new MyPromise((resolve, reject) => {
resolve("success")
})
console.log(test1)
// MyPromise{ promiseResult: 'success', promiseState: 'fulfilled' }
const test2 = new MyPromise((resolve, reject) => {
reject("fail")
})
console.log(test2)
// MyPromise{ promiseResult: 'fail', promiseState: 'rejected' }
const test3 = new MyPromise((resolve, reject) => {
throw "fail"
})
console.log(test3)
// MyPromise{ promiseResult: 'fail', promiseState: 'rejected' }
這里重點說一下initBind中為什么要給resolve和reject綁定this。我們可以看到在resolve和reject中使用了this.promiseState和this.promiseResult。我們需要把它的this綁定到實例才對。
狀態(tài)不可變
如果我們執(zhí)行下面的代碼
const test = new MyPromise((resolve, reject) => {
resolve("success")
reject("fail")
})
console.log(test)
// MyPromise{ promiseResult: 'fail', promiseState: 'rejected' }
這就不符合我們的預期了,因為我們需要的是狀態(tài)不可變。所以我們將代碼改造一下,這里只需要修改resolve和reject就可以了
resolve(val) {
if (this.promiseState !== "pending") return
this.promiseState = "fulfilled"
this.promiseResult = val
}
reject(reason) {
if (this.promiseState !== "pending") return
this.promiseState = "rejected"
this.promiseResult = reason
}當執(zhí)行resolve或reject的時候,發(fā)現(xiàn)狀態(tài)不是pending就說明狀態(tài)已經改變了,直接return即可。
then
我們首先看下原始的Promise的then實現(xiàn)的效果
// 直接輸出success
const p1 = new Promise((resolve, reject) => {
resolve("success")
}).then(
(res) => console.log(res),
(err) => console.log(err)
)
// 1s后輸出fail
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("fail")
}, 1000)
}).then(
(res) => console.log(res),
(err) => console.log(err)
)
// 鏈式調用 直接輸出2000
const p3 = new Promise((resolve, reject) => {
resolve(1000)
})
.then(
(res) => 2 * res,
(err) => console.log(err)
)
.then(
(res) => console.log(res),
(err) => console.log(err)
)由此可以得出結論:
- then 接受兩個回調,一個是成功回調, 一個是失敗的回調
- 當Promise為fulfilled執(zhí)行成功的回調,為rejected 執(zhí)行失敗的回調
- 如果resolve或者reject在定時器里面執(zhí)行,則定時器結束后再執(zhí)行then
- then 支持鏈式調用,下一次then執(zhí)行受上一次then返回值的影響
then實現(xiàn)
由結論1和2可以實現(xiàn)
then(onFulfilled, onRejected) {
// 首先要校驗兩個回調是否是函數(shù)
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)
}
}但是我們如何保證回調是在定時器結束后執(zhí)行呢?首先在定時器結束之前Promise的狀態(tài)一直是pending的,回調結束之后我們才能知道是fulfilled或者是rejected,所以在執(zhí)行then的時候,如果promiseState是pending我們就把回調收集起來,當回調結束之后再觸發(fā)。那使用什么來保存這些回調的呢?這里建議使用數(shù)組,因為一個Promise實例可能會多次執(zhí)行then,用數(shù)組一個個保存就可以了
initValue() {
// 初始化值
this.promiseResult = null
this.promiseState = "pending" // 初始狀態(tài)
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
}
resolve(val) {
if (this.promiseState !== "pending") return
this.promiseState = "fulfilled"
this.promiseResult = val
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(this.promiseResult)
}
}
reject(reason) {
if (this.promiseState !== "pending") return
this.promiseState = "rejected"
this.promiseResult = reason
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.promiseResult)
}
}
then(onFulfilled, onRejected) {
// 首先要校驗兩個回調是否是函數(shù)
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))
}
}鏈式調用
我們再來重新看下鏈式調用的例子
// 鏈式調用 直接輸出2000
const p3 = new Promise((resolve, reject) => {
resolve(1000)
})
.then(
(res) => 2 * res,
(err) => console.log(err)
)
.then(
(res) => console.log(res),
(err) => console.log(err)
)
// 鏈式調用 輸出3000
const p4 = new Promise((resolve, reject) => {
resolve(1000)
})
.then(
(res) => new Promise((resolve, reject) => resolve(3 * res)),
(err) => console.log(err)
)
.then(
(res) => console.log(res),
(err) => console.log(err)
)由此可得:
- then方法本身會返回一個新的Promise對象
- 如果返回值是promise對象,返回值為成功,新promise就是成功
- 如果返回值是promise對象,返回值為失敗,新promise就是失敗
- 如果返回值是非promise對象,新promise對象就是成功,值為此返回值
這里我們對then改造了一下
then(onFulfilled, onRejected) {
// 首先要校驗兩個回調是否是函數(shù)
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 && x) {
throw new Error("不能返回自身")
}
if (x instanceof MyPromise) {
// 如果是promise 返回值為成功 新promise 就是成功
// 如果是promise 返回值失敗 新promise 就是失敗
x.then(resolve, reject)
} else {
// 如果不是promise 直接返回成功
resolve(x)
}
} catch (error) {
reject(error)
throw new Error(error)
}
}
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))
}
})
return thenPromise
}then會返回一個新的Promise,在這個新的promise中定義了方法resolvePromise ,接收一個cb回調函數(shù),這個cb就是傳入的onFulfilled或者onRejected。如果這兩個回調返回的不是promise,那結果直接resolve出去。如果是promise,那么就需要根據(jù)它的狀態(tài)來決定下一步操作。那它到底是成功還是失敗的呢,只有它的then知道。然后把resolve和reject當做回調傳給then,如果x返回的是成功的Promise,則會執(zhí)行resolve, 如果x返回的是失敗的promise,則會執(zhí)行reject。這樣鏈式調用的then才會知道執(zhí)行哪一個回調。
執(zhí)行順序
const p = new Promise((resolve, reject) => {
resolve(1)
}).then(
(res) => console.log(res),
(err) => console.log(err)
)
console.log(2)為了實現(xiàn)類似的功能,使用setTimeout代替
看下完整代碼
class MyPromise {
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
initValue() {
// 初始化值
this.promiseResult = null
this.promiseState = "pending" // 初始狀態(tài)
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(val) {
if (this.promiseState !== "pending") return
this.promiseState = "fulfilled"
this.promiseResult = val
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(this.promiseResult)
}
}
reject(reason) {
if (this.promiseState !== "pending") return
this.promiseState = "rejected"
this.promiseResult = reason
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.promiseResult)
}
}
then(onFulfilled, onRejected) {
// 首先要校驗兩個回調是否是函數(shù)
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason
}
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = (cb) => {
setTimeout(() => {
try {
const x = cb(this.promiseResult)
if (x === thenPromise && x) {
throw new Error("不能返回自身")
}
if (x instanceof MyPromise) {
// 如果是promise 返回值為成功 新promise 就是成功
// 如果是promise 返回值失敗 新promise 就是失敗
x.then(resolve, reject)
} else {
// 如果不是promise 直接返回成功
resolve(x)
}
} catch (error) {
reject(error)
throw new Error(error)
}
})
}
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))
}
})
return thenPromise
}
}其他方法
all
- 接收一個Promise數(shù)組,數(shù)組中如有非Promise項,則此項當做成功
- 如果所有Promise都成功,則返回成功結果數(shù)組
- 如果有一個Promise失敗,則返回這個失敗的結果
static all(promiseList) {
const result = []
const count = 0
return new MyPromise((resolve, reject) => {
const addData = function (index, value) {
result[index] = value
count++
if (count === promiseList.length) resolve(result)
}
promiseList.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
addData(index, res)
},
(err) => {
reject(err)
}
)
} else {
addData(index, promise)
}
})
})
}race
- 接收一個Promise數(shù)組,數(shù)組中有非Promise項,則此項當做成功
- 哪個Promise最快得到結果,就返回那個結果,無論成功失敗
race(promiseList) {
return new MyPromise((resolve, reject) => {
promiseList.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => resolve(res),
(err) => reject(err)
)
} else {
resolve(promise)
}
})
})
}allSettled
- 接收一個Promise數(shù)組,數(shù)組中有非Promise項,則此項當做成功
- 把每個Promise的結果,集合成數(shù)組后返回
allSettled(promiseList) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = function (status, value, i) {
result[i] = {
status,
value
}
count++
if (count === promiseList.length) {
resolve(result)
}
}
promiseList.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
addData("fulfilled", res, index)
},
(err) => {
addData("reject", err, index)
}
)
} else {
resolve(promise)
}
})
})
}any
與all相反
- 接收一個Promise數(shù)組,數(shù)組中如有非Promise項,則此項當做成功
- 如果有一個Promise成功,則返回這個成功結果
- 如果所有Promise都失敗,則報錯
static any(promiseList) {
return new MyPromise((resolve, reject) => {
let count = 0
promiseList.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
resolve(res)
},
(err) => {
count++
if (count === promiseList.length) {
reject("error")
}
}
)
} else {
resolve(promise)
}
})
})
}到此這篇關于JavaScript面試必備技巧之手寫一個Promise的文章就介紹到這了,更多相關JavaScript手寫Promise內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
AngularJs+Bootstrap實現(xiàn)漂亮的計算器
這篇文章主要為大家詳細介紹了angularJs+Bootstrap實現(xiàn)漂亮的計算器,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08

