1. 手写Promise-结构的设计
我们已经学习了Promise的相关使用了,接下来我们来尝试手写一下Promise
首先我们根据Promise的基本调用来找找思路
const newPromise = new Promise((resolve, reject) => {
// resolve("aaaaaa")
reject("err message")
})
我们可以看到,Promise要用new关键字定义,所以我们需要创建一个构造函数或者直接使用class接收一个回调函数,我们之前在学习时知道这个回调函数叫做executor并且在创建时就直接进行调用,它还接受resolve和reject两个函数,所以我们可以开始着手进行一个初步的实现:
class HYPromise {
constructor(executor) {
const resolve = (value) => {
console.log("resolve被调用")
}
const reject = (reason) => {
console.log("reject被调用")
}
executor(resolve, reject)
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("传入的函数被调用了")
resolve(1111)
reject(2222)
})
这里我们发现我们调用resolve和reject他们两都会被调用,但是Promise中一但状态被确定了就不能通过另一个方法来改变,所以我们给它加上一个状态,并且在调用时进行一个状态的判断
// 状态判断
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
const resolve = () => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
console.log("resolve被调用")
}
}
const reject = () => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
console.log("reject被调用")
}
}
executor(resolve, reject)
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111)
reject(2222)
})
2. Promise-then方法设计
对于一个Promise我们想拿到结果必须要有then方法来调用,then方法接受两个回调函数
,并且在resolve和reject中还要接受参数然后在then中进行打印,所以我们就需要在resolve和reject中分别调用两个回调函数
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled(this.value)
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejected(this.reason)
}
}
executor(resolve, reject)
}
// 将两个方法放在类中才能让resolve和reject能够调用
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
return 1111
}, err => {
console.log("err:", err)
})
我们可以看到,在打印结果中只有executor的输出被打印了并且说resolve成功时的函数不是一个方法,这是因为我们在顺序执行代码时我们先执行了Promise中的executor,但是在这里我们又调用了then方法传递的回调函数,但是在这里then方法并没有执行,所以我们并不能拿到这两个回调,于是我们来改进一下
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
// 对状态进行判断
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled(this.value)
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejected(this.reason)
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
这里我们使用了queueMicrotask来创建一个微任务,它会让里面的代码放在下次事件循环的时候再执行,所以then方法就会先执行一次,就可以拿到传入的两个方法了。但是我们在改变状态时要进行判断,因为当这两个微任务要执行的时候他们都会执行,于是resolve和reject都得到了调用我们进行状态区分就失去了意义,我们判断如果状态不是pending的话就直接return
3. Promise-then方法优化
对于上面我们手写的Promise其实还存在很多的问题:
比如我们对返回的Promise进行多次then方法的调用
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
这里只会打印最后一次调用的结果,这是因为我们如果多次对promise进行调用,后面的调用传的回调函数总是会覆盖之前的回调函数,所以我们只能得到最后一次调用的结果,而Promise正常来说不管是调用几次其实都是会执行的,不会互相影响。所以我们应该将这些回调函数存储到数组中,然后循环遍历执行。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
// 循环执行resolve接收的回调函数
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
// 循环执行reject接收的回调函数
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 对接收到的回调函数push进对应数组中
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
这样看来我们好像就解决了多次调用的问题,但其实并没有。我们尝试再来进行一次promise.then的调用,但是这一次我们进行延迟调用
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
setTimeout(() => {
promise.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
但是我们看到这里并没有打印第三次的调用,这是因为进行延迟调用时then里面的回调函数已经完成了添加并且已经进行了打印,所以只会打印前面两次的结果。我们再来进行改进。现在,如果我们要添加回调函数,我们应该在pending状态进行添加。如果状态已经被改变那么我们直接对回调函数进行调用。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
// 循环执行resolve接收的回调函数
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
// 循环执行reject接收的回调函数
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
onRejected(this.reason)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
setTimeout(() => {
promise.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
这样这三次调用就都得到了打印。
4. Promise-then方法优化二
上面我们实现了对promise.then的多次调用,但是我们知道then方法是可以进行链式调用的,因为then方法中给我们重新创建了一个Promise并返回。如果我们在then方法执行完后手动再new一个HYPromise并执行reslove和reject,但是resolve和reject中的传参是来自于上一次执行reslove或者reject的结果。所以,我们在then方法中直接return一个新的HYPromise,如果我们只进行一次调用,但是因为HYPromise的executor会直接执行,所以也能拿到结果,如果有链式的then调用,因为上一次返回的HYPromise已经进行执行了,状态已经发生了改变,所以我们可以在执行回调函数那里获取到上一次回调函数的返回值,于是我们就可以调用resolve并传入上一次的返回值了。这里可能对于reject大家也是直接进行调用,但是在Promise中reject的返回值也是一个Promise,除非我们在reject中抛出异常,它才会进行到下一步的catch中。所以我们这里就可以用try...catch来捕获异常,没有异常我们就调用resolve,有异常我们就调用reject。对于多次调用then我们也要进行处理,这个时候我们就不应该直接在数组中push回调函数了,我们传入一个回调函数,去执行我们在then方法中传入的回调函数,并且也获取返回值并且用try...catch来调用reslove或者reject
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
// 循环执行resolve接收的回调函数
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
// 循环执行reject接收的回调函数
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// 使用try catch捕获异常,没有就进入resolve,有就进入reject
try {
const value = onFulfilled(this.value)
resolve(value)
} catch(err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
// 不再直接push原来then方法传入的回调,传入回调函数去执行then传入的回调
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch(err) {
reject(err)
}
})
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
})
}
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
但是我们在进行new HYPromise实例时直接抛出异常的话就会出问题我们没有对这种情况进行处理。
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
throw new Error("execuse err")
})
其实这里跟之前的处理方式一样,它是因为没有对excutor进行异常处理,那我们就用一个try...catch来捕获异常,有异常就直接调用reject进行处理
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
// 循环执行resolve接收的回调函数
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
// 循环执行reject接收的回调函数
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// 使用try catch捕获异常,没有就进入resolve,有就进入reject
try {
const value = onFulfilled(this.value)
resolve(value)
} catch(err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
// 不再直接push原来then方法传入的回调,传入回调函数去执行then传入的回调
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch(err) {
reject(err)
}
})
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
})
}
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
// resolve(1111)
throw new Error("excutor err message")
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
但是我们有没有发现,我们很多地方都使用到了try..catch并且都是去执行resolve或者reject,导致了现在的代码看起来很臃肿,那么我们就来封装一个方法用try...catch来捕获函数是否有异常,有就执行reject没有就执行resolve
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// resolve(1111)
reject(2222)
// throw new Error("executor error message")
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
5. 手写Promise-catch方法
上面我们实现了Promise的基本方法,现在我们要来实现一下Promise中的catch方法。
首先我们知道catch方法其实就是把then方法中的第二个回调函数去调用reject,所以我们在HYPromise中添加一个catch方法,传入接受一个回调函数,然后调用then方法,第一个参数因为是reslove接受的回调函数,所以我们直接传入undefined:
class HYPromise {
......
catch(onRejected) {
this.then(undefined, onRejected)
}
}
但是这里首先有一个错误,因为我们第一个回调传入的是undefinde,但是我们在then方法中是直接传入我们的工具函数去执行的,但是undefinde不是函数,没法执行,于是就会报错。所以我们在pending状态下执行回调之前进行判断再回调
if (this.status === PROMISE_STATUS_PENDING) {
if(onFulfilled) this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
if(onRejected) this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
});
}
然后正常进行then与catch的调用打印
promise.then(res => {
console.log("res", res);
}).catch(err => {
console.log("err", err);
})
结果catch并没有执行。
这是因为,我们之前获取reject的返回是要在then方法的第二个参数传入一个回调,但是这里只传了resolve的回调,并且catch方法是在then回调完成之后再进行调用的,我们上面再then方法中返回的是一个新的HYPromise,而且它里面是没有err的回调的,catch方法传入的err回调被放入到了第二个HYPromise中,当然就不能拿到返回结果。我们现在就要想办法让第二个HYPromise拿到第一个HYPromise的err信息。我们上面不是完成了then方法的链式调用,因为then返回的是HYPromise,我们当时想要在第二个then方法的调用拿到err信息,我们是采用在reject接收的回调函数中抛出异常的方式,所以我们这里在then方法中进行判断,如果第二个回调是undefinde,我们直接将当前的err信息变成异常抛出,这样就能在第二次调用then方法时拿到上一次状态已经变成rejected并且err信息为上一次的err信息。
于是:
then(onFulfilled, onRejected) {
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
return new HYPromise((resolve, reject) => {
...
})
}
完整代码:
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled) this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
if (onRejected) this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
catch(onRejected) {
this.then(undefined, onRejected)
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// resolve(1111) // resolved/fulfilled
reject(2222)
})
// 调用then方法多次调用
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
6. 手写Promise-finally方法
我们刚刚完成了catch方法的实现,现在我们就来尝试实现finally方法,在尝试实现之前,我们还得来解决一下之前代码的问题,如果按上面的代码来看的话我们是不能执行finally的,因为我们的catch方法没有返回HYPromise,想改进其实也很简单,直接在前面加上return就好了
catch(onRejected) {
return this.then(undefined, onRejected)
}
我们来看看finally方法,它是不管是then方法还是catch它都会执行,所以我们在直接在类中添加finally方法,它直接调用then并且两个参数都是回调函数直接执行finally中的回调。
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
我们再来进行调用
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending");
resolve(1111) // resolved/fulfilled
// reject(2222);
// throw new Error("executor error message")
});
promise.then(res => {
console.log("res1:", res)
return "aaaaa"
}).then(res => {
console.log("res2:", res)
}).finally(() => {
console.log("finally")
})
但是我们上面没有调用catch,那么我们加上去看看
promise.then(res => {
console.log("res1:", res)
return "aaaaa"
}).then(res => {
console.log("res2:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally")
})
catch调用后面都没有执行,但是我们一开始promise中是reject调用
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending");
// resolve(1111);
reject(2222);
});
promise
.then((res) => {
console.log("res1:", res);
return "aaaaa";
})
.then((res) => {
console.log("res2:", res);
})
.catch((err) => {
console.log("err:", err);
})
.finally(() => {
console.log("finally");
});
打印结果又没问题了
可见问题出在了catch与成功时回调的resolve上,那么是什么原因导致的呢。
还记不记得我们在catch中的写法,因为我们在cath中执行then的时候成功的回调是undefinde这就导致了我们虽然返回了一个HYPromise但是它的reslove已经没有回调函数可以执行了,成功的参数在这里就断掉了。那么我们要解决它,就应该让没有resolve的回调函数时直接返回HYPromise的value值,让后面拿到的HYPromise是有成功参数的,于是我们的做法就跟之前catch时一样,在then方法返回HYPromise之前进行判断然后操作。
then(onFulfilled, onRejected) {
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
...
}
其实我们这里就已经对两个回调函数进行了判断,后面return中一定是有两个回调函数的
完整代码:
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled) this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
if (onRejected) this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111)
// reject(2222)
})
promise.then(res => {
console.log("res1:", res)
return "aaaaa"
}).then(res => {
console.log("res2:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally")
})
7. 手写Promise-resolve-reject方法
Promise的resolve和reject方法是只能通过Promise去调用,不能通过实力对象去调用。所以resolve和reject都应该是属于Promise中的静态类方法。他们实现的功能也很简单,就是返回一个Promise并且调用相关的reslove和reject
// 在HYPromise类中添加
static resolve(value) {
return new HYPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HYPromise((resolve, reject) => reject(reason))
}
执行调用
HYPromise.resolve("Hello World").then(res => {
console.log("res:", res)
})
HYPromise.reject("Error Message").catch(err => {
console.log("err:", err)
})
8. 手写Promise-all-allSettled方法
接下我们来手写all跟allSettled方法。老样子,我们先分析他们属于什么方法,他们也都是通过Promise.xxx()的方式来调用的所以他们都是静态类方法。
接下来我们先分析all方法。all方法接收一个数组,数组的内容都是promise的实例对象,当拿到所有结果并且都是成功就将所有的结果放进数组里并进行打印,一旦有错误返回则直接返回并打印错误结果。注意,all方法也是要用then方法来搜集结果,所以all方法返回的也是一个Promise。
所以它的实现其实就很简单了,返回一个HYPromise,executor中来判断什么时候调用reslove,什么时候调用reject。因为接受的参数是一个HYPromise的实力对象数组,所以我们对它进行遍历,并且调用then方法去搜集结果并放入到我们的结果数组中,调用resolve就需要去判断我们拿到的结果数量是否跟传入的实例对象数量相等,相同时就进行reslove的调用。在这个过程中一但被第二个回调,也就是错误时的回调搜集到时我们就直接进行reject的调用
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new HYPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if (values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
我们来进行测试
const p1 = new Promise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new Promise((resolve) => {
setTimeout(() => { resolve(2222) }, 2000)
})
// const p2 = new Promise((resolve, reject) => {
// setTimeout(() => { reject(2222) }, 2000)
// })
const p3 = new Promise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
})
HYPromise.all([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
这里我们都进行延迟调用时为了更好的去区分resolve和reject
** 全是成功时的返回**:
错误被拦截时的返回:
接下来我们就来分析allSettled
allSettled其实跟all很相似,只是allSettled不管有没有异常都会等到全部执行完成再返回结果,并且结果是对象数组,每个对象中都有当前实例对象的状态与返回值,而且allSettled是没有catch调用的,所以allSettled中只会去调用reslove方法。
static allSettled(promises) {
return new HYPromise((resolve) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: PROMISE_STATUS_FULFILLED, value: res})
if (results.length === promises.length) {
resolve(results)
}
}, err => {
results.push({ status: PROMISE_STATUS_REJECTED, value: err})
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
调用:
const p1 = new Promise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
})
HYPromise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
})
9. 手写Promise-race-any方法
race和any跟all和allSettled类似,所以这里就简短说明。
race是只要有一个Promise实例有结果了,不管是成功还是异常都直接进行打印所以它的实现也就很显而易见了
static race(promises) {
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
promise.then(resolve, reject)
})
})
}
这里把回调函数的部分做了个简写,因为他直接执行的话传入的回调函数跟外面接收的回调函数完全一样,里面的结果也一样,所以就没有必要再拿到结果然后调用,直接传进去就可以了。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => { reject(1111) }, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => { reject(3333) }, 3000)
})
HYPromise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
any的话跟race也差不多只是它必须得有一个成果的结果才返回,如果全是错误它就会合并错误并返回
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
这里用到了ES12中的AggregateError他就是用来做错误合并的,拿到的错误调用.errors得到错误结果的数组
const p1 = new Promise((resolve, reject) => {
setTimeout(() => { reject(1111) }, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => { reject(3333) }, 3000)
})
HYPromise.any([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err.errors)
})
到这里,我们的手写Promise就差不多完成了,主要的难点还是Promise a+规范的原生then方法,需要考虑的情况比较多,ES6以后添加的一些方法反倒实现起来就没那么难。掌握了then方法的基本上就大头朝下了。最后附上完整的手写Promise代码
// ES6 ES2015
// https://promisesaplus.com/
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled) this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
if (onRejected) this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
static resolve(value) {
return new HYPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HYPromise((resolve, reject) => reject(reason))
}
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new HYPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if (values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
static allSettled(promises) {
return new HYPromise((resolve) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: PROMISE_STATUS_FULFILLED, value: res})
if (results.length === promises.length) {
resolve(results)
}
}, err => {
results.push({ status: PROMISE_STATUS_REJECTED, value: err})
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
static race(promises) {
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}