很多同学在面试的时候都会被要求手写一个Promise,那么今天我总结了一些手写Promise的方法,可以跟着我的思路一起来实现一个Promise,让我们的面试更有把握。同时我们也会实现一下Promsie常见的方法比如:all、race、allSettled、any。
基本实现
首先我们可以用类来实现Promise,而且Promise有三种状态:pending、fulfilled、rejected。初始状态为pending。还需要对Promise的终值进行初始化。Promise还有两个方法resolve和reject。
Promise有四个特点:
- 执行了resolve,Promise状态就会变成fulfilled
- 执行了reject,Promise状态就会变成rejected
- Promise状态不可逆,第一次成功就永久为fulfilled,第一次失败就永久为rejected
- Promise中有throw的话,就相当于执行了rejected
下面我就来简单的实现一下吧
实现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" // 初始状态 } 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绑定到实例才对。
状态不可变
如果我们执行下面的代码
const test = new MyPromise((resolve, reject) => { resolve("success") reject("fail") }) console.log(test) // MyPromise{ promiseResult: 'fail', promiseState: 'rejected' }
这就不符合我们的预期了,因为我们需要的是状态不可变。所以我们将代码改造一下,这里只需要修改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 }
当执行resolve或reject的时候,发现状态不是pending就说明状态已经改变了,直接return即可。
then
我们首先看下原始的Promise的then实现的效果
// 直接输出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执行成功的回调,为rejected 执行失败的回调
- 如果resolve或者reject在定时器里面执行,则定时器结束后再执行then
- then 支持链式调用,下一次then执行受上一次then返回值的影响
then实现
由结论1和2可以实现
then(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) } }
但是我们如何保证回调是在定时器结束后执行呢?首先在定时器结束之前Promise的状态一直是pending的,回调结束之后我们才能知道是fulfilled或者是rejected,所以在执行then的时候,如果promiseState是pending我们就把回调收集起来,当回调结束之后再触发。那使用什么来保存这些回调的呢?这里建议使用数组,因为一个Promise实例可能会多次执行then,用数组一个个保存就可以了
initValue() { // 初始化值 this.promiseResult = null this.promiseState = "pending" // 初始状态 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) { // 首先要校验两个回调是否是函数 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) { // 首先要校验两个回调是否是函数 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回调函数,这个cb就是传入的onFulfilled或者onRejected。如果这两个回调返回的不是promise,那结果直接resolve出去。如果是promise,那么就需要根据它的状态来决定下一步操作。那它到底是成功还是失败的呢,只有它的then知道。然后把resolve和reject当做回调传给then,如果x返回的是成功的Promise,则会执行resolve, 如果x返回的是失败的promise,则会执行reject。这样链式调用的then才会知道执行哪一个回调。
执行顺序
const p = new Promise((resolve, reject) => { resolve(1) }).then( (res) => console.log(res), (err) => console.log(err) ) console.log(2)
为了实现类似的功能,使用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" // 初始状态 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) { // 首先要校验两个回调是否是函数 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数组,数组中如有非Promise项,则此项当做成功
- 如果所有Promise都成功,则返回成功结果数组
- 如果有一个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数组,数组中有非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数组,数组中有非Promise项,则此项当做成功
- 把每个Promise的结果,集合成数组后返回
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数组,数组中如有非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) } }) }) }