前言
Promise的手写是面试中一个常考的考点。希望我的文章能够帮到大家,在被问到Promise时能够露出一个自信的微笑。
constructor
首先来写一个Promise的构造器,这有帮助于我们了解Promise的内部结构和他各个方法的底层原理。
js
class myPromise{
constructor(executor){
this.state = 'pending' // 记录promise的状态,pending...等
this.value = undefined // resolve传递的值
this.reason = undefined // reject传递的值
this.onFullfilledCallback = [] // resovle的回调函数
this.onRejectedCallback = [] // rejected的回调函数
const resolve = (value)=>{
if(this.state === 'pending'){
this.state = 'fulfilled'// 判断当前对象状态
this.value = value
this.onFullfilledCallback.forEach(callback=>callback(value))
}
}
const reject = (reason)=>{
if(this.state === 'pending'){
this.state = 'rejected'// 判断当前对象状态
this.reason = reason
this.onRejectedCallback.forEach(callback=>callback(reason))
}
}
executor(resolve, reject) // 执行器函数
}
}
这种手写的话我们都要想一想我们平时使用promise
有哪些特点。首先我们知道
- promise有三种状态,pending,fulfilled,reject
- promise状态一旦修改无法二次修改(为了确认异步操作的正确性)
- resolve和reject可以接受一个回调函数 基于这些我们就写出了以上这些代码。
then
js
then(onFulfilled,onRejected){
// 判断是否传递了一个回调参数
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const newPromise = new myPromise((resolve, reject) => {
// 考虑onFullfilled,onRejected
if (this.state === 'fulfilled') {// then前面的对象状态已经变更
setTimeout(() => {// 模拟异步但是模拟不了微任务
try {
const result = onFullfilled(this.value)
resolve(result)
} catch (e) {
reject(e)
}
});
}
if (this.state === 'rejected') {
setTimeout(() => {// 模拟异步但是模拟不了微任务
try {
const result = onRejected(this.reason)
resolve(result)
} catch (e) {
reject(e)
}
});
}
if (this.state === 'pending') {
this.onFullfilledCallback.push((value) => {
setTimeout(() => {
try {
const result = onFullfilled(value)
resolve(result)
} catch (error) {
reject(error)
}
});
})
this.onRejectedCallback.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(reason)
resolve(result)
} catch (error) {
reject(error)
}
});
})
}
})
return newPromise
}
- then中可以接受两个参数,当上一个promise对象为fulfilled时调用第一个回调函数,rejected调用第二个回调函数
- then需要返回一个新的promise对象并且返回和上一个promise对象resolve或者rejected对应的值
catch
kotlin
catch(onRejected) {
this.then(null, onRejected)
}
race
js
// race 只找最快的,无论是resolve和rejected都只取最快的一个
static race(promises) {
return new myPromise((resolve, reject) => {
promises.forEach(promise => {
// promise的状态是不是fulfilled
promise.then((value) => {
resolve(value)
}, (reason) => {
reject(reason)
})
})
})
}
- race会返回一个新的promise对象
- race接受一个数组为参数,数组每一个元素都要是一个promise对象
- race会取数组中速度最快的一个为结果并返回,无论是promise还是rejected
all
js
// all 当所有的都是resolve就会以数组形式返回所有的值,有一个reject直接reject
static all(promises) {
return new myPromise((resolve, reject) => {
let count = 0
let arr = []
// 判断数组中所有的promise状态是否都为fulfilled
promises.forEach((promise, i) => {
promise.then((value) => {
count++
arr[i] = value
if (count === promise.length) {
resolve(arr)
}
}, (reason) => {
reject(reason)
})
})
})
}
- all也会返回一个新的promise对象且也接受一个元素全为promise的数组为参数
- 当数组中所有元素的状态都为resolve时就返回所有的数组元素,有一个为rejected就返回那一个rejected的对象,无论哪种情况都要给返回的peomise对象设置对应的状态。
any
js
// 只要有一个能resolve就直接resolve,全部reject才reject
static any(promises) {
return new myPromise((resolve, reject) => {
let count = 0
let arr = []
promises.forEach((promise, i) => {
promise.then((value) => {
resolve(value)
}, (reason) => {
count++
arr[i] = reason
if (count === promises.length) {
reject(new AggregateError(arr, 'All promise were rejected'))
}
})
})
})
}
- any和all相反,只要有一个元素状态为resolve就直接返回那一个resolve对象并用一个新的promise对象包裹且设置对应的状态
- 全为rejected则返回整个数组也是promise对象包裹且状态设置为rejected
finally
javascript
// finally在前一个promise对象结束时,无论是fulfilled还是rejected都会调用内部回调
finally(callback) {
return this.then(() => {
(value) => { return Promise.resolve(callback()).then(() => value) }
}, () => {
(reason) => { return Promise.resolve(callback()).then(() => reason) }
})
}
- finally对于上一个promise对象无论他的值为什么都要调用其回调函数
- 并且要返回其对应的resolve或者rejected对应的值
allSettled
js
static allSettled(promises) {
let arr = []
let count = 0
return new myPromise((resolve, reject) => {
promises.forEach((promise, i) => {
promise.then((value) => {
arr[i] = { status: 'fulfilled', vlaue: value }
}, (reason) => {
arr[i] = { status: 'rejected', reason: reason }
}).finally(() => {
count++
// 所有promise状态都变更
if (count === promises.length) {
resolve(arr)
}
})
})
})
}
- allSettled接受一个数组为参数
- 当数组中所有元素的状态都发生变更时才会调用其内部的回调并返回一个新的promise对象
- 返回的对象只要发生状态变更一定是fulfilled
尾声
Promise主要的几个手写方法就是这样,对于这种手写题我们一定要思考我们平时使用时有哪些特征,并根据这些特征一步步编写我们的代码,最后就能完全成型。