写在最前
promiseA+
实现其实很简单,就是里面回调函数的设计有点点绕,相信看完本文,一定会有所收获!本文主要是参考coderwhy大神的授课加上自己的理解。
Promise作用
Promise的出现解决了什么问题呢,解决的就是我们发送网络请求时,我们需要自己设计回调函数,而每个人所设计的回调函数方式不同,使我们使用异步函数执行回调时更加的规范。
Promise
是一个类,可以翻译成 承诺、许诺 、期约- 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个
Promise
的对象; - 在通过new创建
Promise
对象时,我们需要传入一个回调函数,我们称之为executor
- 这个回调函数会被立即执行,并且给传入另外两个回调函数
resolve
、reject
; - 当我们调用
resolve
回调函数时,会执行Promise
对象的then
方法传入的回调函数; - 当我们调用
reject
回调函数时,会执行Promise
对象的catch
方法传入的回调函数;
- 这个回调函数会被立即执行,并且给传入另外两个回调函数
Promise的结构设计
//定义常量,表示promise的三种不同状态
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class MyPromise {
//构造函数所传入的executor就是我们平时调用Promise传入的函数
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
//我们知道promise状态一经改变,便不能再次改变状态
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
console.log('resolve被调用')
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
console.log('reject被调用')
}
}
//这个executor会被直接执行,并且传入我们定义的resolve函数和reject函数
executor(resolve, reject)
}
}
//测试代码
//执行结果:
//状态pending
//resolve被调用
const promise = new MyPromise((resolve, reject) => {
console.log('状态pending')
resolve(1111)
reject(2222)
})
Promise的then方法
实现then方法的第一步
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class MyPromise {
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
//queueMicrotask微任务
queueMicrotask(() => {
//只能开启一个微任务才能拿到this.onFulfilled
//then方法是同步被调用,如果不开启微任务,那么this.onFulfilled将会为undefined
this.value = value
this.onFulfilled(this.value)
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
//只能开启一个微任务才能拿到this.onRejected
//then方法是同步被调用,如果不开启微任务,那么this.onRejected将会为undefined
this.reason = reason
this.onRejected(this.reason)
})
}
}
executor(resolve, reject)
}
//then方法是被我们同步调用的,比如
//Promise.resolve('aaa').then((res)=>{log(res),(err)=>{log(err)})
//那么我们在then中传递的两个函数是怎么在Promise resolve/reject之后被调用呢
//答案就是开启一个微任务
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new HYPromise((resolve, reject) => {
console.log('状态pending')
reject(2222)
resolve(1111)
})
// 测试代码
//执行结果:
//状态pending
//err: 2222
promise.then(
(res) => {
console.log('res1:', res)
},
(err) => {
console.log('err:', err)
}
)
优化then方法
问题1
: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)
})
问题2
:Promise
状态确定之后也是可以调用then
方法的
const promise = new Promise((resolve, reject) => {
resolve("aaaaa")
})
// 在确定Promise状态之后, 再次调用then
setTimeout(() => {
promise.then(res => {
console.log("res:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
//执行结果:res:aaaaa
所以我们要对上一份代码进行优化
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
//这里我们使用两个数组保存传入的函数(对应then方法多次被调用)
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
//为什么要加此判断呢?
//这是为了Promise状态改变之后,防止执行另一状态对应的回调函数
//因为我们将Promise的状态改变放在了微任务里面
if (this.status !== PROMISE_STATUS_PENDING) return
//为什么要把状态的改变提在里面来呢?
//因为我们为了解决问题2,在下面添加回调函数时进行了判断,
//如果在外面改变状态,那么后续本来要添加的回调函数将直接被执行
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)
})
})
}
}
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 mYPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111) // resolved/fulfilled
reject(2222)
})
//测试代码:
// 调用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)
})
// 在确定Promise状态之后, 再次调用then
setTimeout(() => {
promise.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
//执行结果:
//状态pending
//res1: 1111
//res2: 1111
//res3: 1111
再次优化then方法
问题:返回的promise
是可以进行链式调用的:
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)
}
)
我们怎么使我们的promise
能够链式调用呢?那么就要对then
方法的返回值做文章了,上一份代码then
方法是没有返回任何东西的,试想一下,只要使我们的then
方法继续返回promise
,那不就解决了吗!
注意
then
方法返回prmoise
,那么这个promise
什么时候resolve
,什么时候reject
呢?
then
方法传入的回调函数是可以有返回值的,resolve
这个值;如果then
方法传入的回调函数执行过程出错就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 MyPromise {
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)
})
})
}
}
//如果在执行executor过程中出现错误,调用reject
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
//返回值直接在new一个MyPromise,直接解决问题
return new MyPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
//如果传入函数执行过程中出错,调用reject
//否者对resolve函数执行结果
// 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) {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new MyPromise((resolve, reject) => {
console.log('状态pending')
reject(2222)
})
// 调用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)
}
)
//执行结果:
//状态pending
//err1: 2222
//res2: bbbbb
catch方法
catch(onRejected) {
return this.then(undefined, onRejected)
}
//catch方法如此之简单,但是仅仅这样做会出现一个问题
//试想如果是以下代码:
const promise = new MyPromise((resolve, reject) => {
console.log('状态pending')
reject(2222)
})
promise
.then((res) => {
console.log('res:', res)
})
.catch((err) => {
console.log('err:', err)
})
//promise的then中并未传入第二个函数,那么执行结果仅仅是:状态pending
//因为我们then方法中返回的promise会在传入执行函数执行之后调用resolve/reject
//如果不给then传入第二个参数,那么then返回的promise将会一直处于pendding状态
//自然不会调用后续的catch传入的函数
为解决上述问题,我们可以再修改一下then
then(onFulfilled, onRejected) {
//仅仅设置一个onRejected默认值,将上一个promise reject的错误抛出即可
const defaultOnRejected = (err) => {
throw err
}
onRejected = onRejected || defaultOnRejected
return new MyPromise((resolve, reject) => {
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)
}
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)
})
}
})
}
此时出现我们想要的结果
//测试代码
const promise = new MyPromise((resolve, reject) => {
console.log('状态pending')
reject(2222)
})
promise
.then((res) => {
console.log('res:', res)
})
.catch((err) => {
console.log('err:', err)
})
//执行结果:
//状态pending
//err: 2222
还有一个类似的问题,由于catch
方法内部调用then
,但是没有传入onFulfilled
,那么在catch
方法后续继续链式调用也会得不到结果
const promise = new MyPromise((resolve, reject) => {
console.log('状态pending')
resolve(1111)
})
// 调用then方法多次调用
promise
.then((res) => {
console.log('res:', res)
})
.catch((err) => {
console.log('err:', err)
return 'aaa'
})
.then((res2) => {
console.log(res2, 'res')
})
//执行结果:
//状态pending
//res: 1111
这个时候还是老样子,只给onFulfilled
设置一个默认值即可
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
finally
finally(onFinally) {
this.then(onFinally
,onFinally)
}
//如此简单
Promise类上的静态方法
resolve、reject
我们会为了方便直接拿到一个promise,常常会这样使用Promise.resolve('xiaozhao').then((res)=>{log(res)})
//没错,就是这么简单明了
static resolve(value) {
return new HYPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HYPromise((resolve, reject) => reject(reason))
}
//测试代码
MyPromise.resolve("Hello World").then(res => {
console.log("res:", res)
})
MyPromise.reject("Error Message").catch(err => {
console.log("err:", err)
//执行结果:
//res: Hello World
//err: Error Message
})
all
all
方法返回一个promise
,并且接受一个数组(里面存放promise
,也可以是普通值)
当数组里面的promise
都被reslove
以后,all
返回的promise
resolve
,否者reject
static all(promises) {
return new MyPromise((reslove, reject) => {
if (typeof promises[Symbol.iterator] !== 'function') {
reject('err')
}
if (promises.length === 0) {
reslove([])
}
const value = []
promises.forEach((promise) => {
//这里有可能传的是别的,不是promise,可能是基本数据类型,可以包裹一下
//这里用原生Promise包裹,因为我们实现的MyPromise没有考虑resolve promise
//当然我们也可以做,在resolve里面判断一下是否是promise
//如果是就promise.then(resolve,reject)
Promise.resolve(promise).then(
(res) => {
value.push(res)
if (value.length === promises.length) {
reslove(value)
}
},
(err) => {
reject(err)
}
)
})
})
//测试代码
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(1111)
}, 3000)
})
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(2222)
}, 2000)
})
const p3 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(3333)
}, 3000)
})
MyPromise
.all([p1, p2, p3])
.then((res) => {
console.log('res:', res)
})
.catch((err) => {
console.log('err:', err)
})
//执行结果
//err: 2222
allSettled
返回值和参数都和all
一样,返回的promise
一定是fulfilled
状态
原理:等到传入的所有promise
都有结果,并且一定执行resolve
static allSettled(promises) {
return new MyPromise((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)
}
}
)
})
})
}
race/any
static race(promises) {
//只要有结果,立即resolve/reject
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
// console.log(resolve.)
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, (err) => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
所有代码
// 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 dyPromise {
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 dyPromise((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 dyPromise((resolve) => resolve(value))
}
static reject(reason) {
return new dyPromise((resolve, reject) => reject(reason))
}
static all(promises) {
return new dyPromise((reslove, reject) => {
if (typeof promises[Symbol.iterator] !== 'function') {
reject('err')
}
if (promises.length === 0) {
reslove([])
}
const value = []
promises.forEach((promise) => {
//这里有可能传的是别的,不是promise
Promise.resolve(promise).then(
(res) => {
value.push(res)
if (value.length === promises.length) {
reslove(value)
}
},
(err) => {
reject(err)
}
)
})
})
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new dyPromise((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 dyPromise((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 dyPromise((resolve, reject) => {
promises.forEach((promise) => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
// console.log(resolve.)
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new dyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, (err) => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
写在最后
一道面试题,看到很多大佬都在讨论
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4)
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
})
// 0
// 1
// 2
// 3
// 4
// 5
// 6
为什么会有这样的执行结果呢,是因为js引擎做了优化,对return Promise.resolve(4)
加了两次微任务,为什么会做这样的优化呢,请看我娓娓道来
Promise.resolve()
.then(() => {
console.log(0)
return 4
})
.then((res) => {
console.log(res)
})
Promise.resolve()
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3)
})
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
// 0
// 1
// 4
// 2
// 3
// 5
// 6
Promise.resolve()
.then(() => {
console.log(0)
//return thenable的值
return {
then: function (resolve) {
// 大量的计算
resolve(4)
}
}
})
.then((res) => {
console.log(res)
})
Promise.resolve()
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3)
})
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
// 0
// 1
// 2
// 4
// 3
// 5
// 6
可见return
一个thenable
的对象,会加一次微任务,因为thenable
函数中的有可能存在耗时操作(比如有大量的计算),那么就有可能阻塞后续的微任务,所以加一次微任务
你可能有疑问,加一次微任务,虽然不会阻塞后一次微任务,但是如果后一次微任务中又添加微任务(就像这道面试题),那不是又阻塞住了?
事实上这是一种特殊情况,我们现实中的代码是不会接连着微任务中加微任务的
所以就可以理解return
一个thenable
的对象,会加一次微任务;那么我们就知道then
中返回特殊值会加一次微任务,那么返回一个promise
算是特殊值(加一次微任务),而promise
本身加一次微任务,所以一共加两次微任务,不得不感叹v8的强大。
希望对你有帮助