前言
- 最近通过学习javascrip的异步处理了解到了Promise对象,并学习了对Promise原理的分析,以此记录!
Promise的原理解析
- 通过封装库的思想手动构造一个Promise我学习到从以下几个方面去考虑
- Promise 中三种状态的实现
- Promise 中then的实现方法
- Promsie 中一些方法
一、Promise 的三种状态
- Promise中默认 pending 状态,调取 resolve方法 成功返回 fulfilled 状态,调取 reject方法 成功返回 rejected 状态
// Promise类
let p = new Promise((resolve, reject) => {
// resolve('success')
// reject('err')
})
console.log(p) // 返回Promise对象 其中[[PromiseState]]: "pending" [[PromiseResult]]: undefined
根据返回对象我们可以自己构造一个方法实现简单的展现Promise的状态
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
}
}
let p = new MPromise((resolve, reject) => {
// resolve('success')
reject('err')
})
console.log(p) //
这样就实现了一个简易的呈现状态的Promise
二、then的问题
- 多个 then 的实现
- then 的返回值
- 微任务宏任务
- 链式操作
实现then方法
Promise中这样调取
let p = new Promise((resolve, reject) => {
resolve('success')
reject('err')
})
p.then((res) => {
console.log(res) // success
},err=>{
console.log(err) // err
})
手动构造一个 then 的方法 注意:then 方法不能立刻执行,当调取resolve或者reject之后才能执行 then 否则直接执行 then 延迟执行resolve或reject时会抛出undefined,代码如下:
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
}
then(onResolved, onRejected) {
if ((this['[[PromiseState]]'] = 'fulfilled')) {
onResolved && onResolved(this['[[PromiseResult]]'])
} else {
onRejected && onRejected(this['[[PromiseResult]]'])
}
}
}
let p = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
// reject('err')
})
})
p.then((res) => {
console.log(res)
}
那么解决这一问题的方法就是在调取resolve或者reject后执行,代码如下:
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.resolveFn = undefined
this.rejectFn = undefined
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
this.resolveFn(val)
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
this.resolveFn(val)
}
then(onResolved, onRejected) {
// 将状态保存起来
this.resolveFn = onResolved
this.rejectFn = onRejected
}
}
这样我们延迟调用resolve或reject就不会出现undefined现象
多个then的问题
上面的方法虽然解决延时调用的问题,但如果我们调用多个 then 方法会出现覆盖问题,总是获取到最后一次调取的值,所以多个 then 的解决方法就是通过数组存储,依次执行
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.resolveQueue = []
this.rejectQueue = []
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(val)
}
}
run()
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(err)
}
}
run()
}
then(onResolved, onRejected) {
// 将状态保存起来
this.resolveQueue.push(onResolved)
this.rejectQueue.push(onRejected)
}
}
let p = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
// reject('err')
})
})
p.then((res) => {
console.log(res)
})
p.then((res) => {
console.log('22')
})
但是这样还有一个执行顺序问题,如果同步执行的话,最后执行 then 方法,导致同步执行没有结果,解决这一问题的方法就是先执行 then 方法,所以让resolve和reject变成异步方法,代码如下:
// 将上面代码中run方法修改为异步方法即可
setTimeout(run)
这样不论是同步还是异步,调用 then 都会获取结果
微任务与宏任务
上面我们解决了同步和异步时都会调用 then 方法的问题,然而还有一个问题需要解决,先看问题:
// 输出顺序问题
// Promise
setTimeout(() => {
console.log('11')
})
console.log('22')
let p = new Promise((resolve, reject) => {
resolve('33')
})
p.then((res) => {
console.log(res)
})
p.then((res) => {
console.log('44')
})
console.log('55')
// 输出顺序为 22-55-33-44-11
// 自定义方法
// MPromise为上述方法
setTimeout(() => {
console.log('11')
})
console.log('22')
let p = new MPromise((resolve, reject) => {
resolve('33')
})
p.then((res) => {
console.log(res)
})
p.then((res) => {
console.log('44')
})
console.log('55')
// 输出顺序为 22-55-11-33-44
- 微任务:一个需要异步执行的函数,执行时机是在主函数执行结束自后、当前宏任务结束之前
- 宏任务:宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
then 方法是一个微任务
- 执行时 首先是同步代码执行,所以先输出 22 55 之后执行异步代码,因为一个宏任务执行后要先执行它所包含的微任务,然后在去执行下一个宏任务,setTimeout属于下一个宏任务,所以先输出 33 44
那如何修改自己构造的方法呢?
MutationObserver 方法
developer.mozilla.org/zh-CN/docs/…
利用这个方法我们可以通过监听属性变化,进行函数调用,变成异步微任务,代码如下:
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.resolveQueue = []
this.rejectQueue = []
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(val)
}
}
const observe = new MutationObserver(run)
observe.observe(document.body, { attributes: true })
document.body.setAttribute('name', 'a')
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(err)
}
}
const observe = new MutationObserver(run)
observe.observe(document.body, { attributes: true })
document.body.setAttribute('name', 'a')
}
then(onResolved, onRejected) {
// 将状态保存起来
this.resolveQueue.push(onResolved)
this.rejectQueue.push(onRejected)
}
}
setTimeout(() => {
console.log('11')
})
console.log('22')
let p = new MPromise((resolve, reject) => {
resolve('33')
})
p.then((res) => {
console.log(res)
})
p.then((res) => {
console.log('44')
})
console.log('55')
// 通过MutationObserver模拟一个微任务,所以输出顺序就是 22-55-33-44-11
链式操作
先来看一下 Promise 中 then 的链式操作
let p = new Promise((resolve, reject) => {
resolve('success')
})
p.then((res) => {
console.log(res)
return new Promise((resolve, reject) => {
resolve('success')
})
}).then((res) => {
console.log(res)
})
// 打印两个success
也就是说在 Promise 的 then 中可以通过 return 返回并通过下一个 then 调取,所以我的思路就是先让自己构造的 then 方法可以返回一个新包装的 Promise 对象 代码如下:
class MPromise {
constructor(handle) {
// 默认状态
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.resolveQueue = []
this.rejectQueue = []
handle(this.#resolve.bind(this), this.#reject.bind(this))
}
// 成功时调取函数
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(val)
}
}
const observe = new MutationObserver(run)
observe.observe(document.body, { attributes: true })
document.body.setAttribute('name', 'a')
}
// 失败时调取函数
#reject(err) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
const run = () => {
let cb
while ((cb = this.resolveQueue.shift())) {
cb && cb(err)
}
}
const observe = new MutationObserver(run)
observe.observe(document.body, { attributes: true })
document.body.setAttribute('name', 'a')
}
then(onResolved, onRejected) {
return new MPromise((resolve, reject) => {
// 拿到结果 且不可立即执行
let resolveFn = function (val) {
let result = onResolved && onResolved(val)
// 返还MPromise对象
if (result instanceof MPromise) {
result.then(resolve)
} else {
resolve(result)
}
}
this.resolveQueue.push(resolveFn)
let rejectFn = function (err) {
onRejected && onRejected(err)
reject(err)
}
this.rejectQueue.push(rejectFn)
})
// 将状态保存起来
}
}
let p = new MPromise((resolve, reject) => {
resolve('33')
})
p.then((res) => {
console.log(res)
return new MPromise((resolve) => {
resolve('11')
})
}).then((res) => {
console.log(res)
})
// 结果打印 33,11
三、周边方法
原型方法
catch
catch主要是通过 then 方法实现的 通过给 then 中的onReject传入值即可
catch(fn) {
return this.then(undefined, fn)
}
let p = new MPromise((resolve, reject) => {
reject('err')
})
p.then((res) => {
console.log(res)
}).catch((err) => {
console.log(err) // err
})
finally
finally(cb) {
this.then(cb, cb)
}
finally方法放在then后面执行就好
静态方法
resolve/reject
也是通过返回一个 MPromise 对象来构造的方法
static resolve(val) {
return new MPromise((resolve) => {
resolve(val)
})
}
static reject(err) {
return new MPromise((resolve, reject) => {
reject(err)
})
}
let p = MPromise.reject('err')
console.log(p) // 返回一个 MPromise 对象
race
race 的参数是一个包含Promise对象的数组
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err')
}, 1000)
})
Promise.race([p1, p2]).then(
(res) => {
console.log(res)
},
(err) => {
console.log(err)
}
)
// 结果打印 err
自己实现的思路就是定义一个静态函数返回Promise对象
static race(lists) {
return new MPromise((resolve, reject) => {
lists.forEach((item) => {
item.then(
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
})
}
all
只返回resolve中的值
static all(lists) {
let resArr = []
let num = 0
return new MPromise((resolve) => {
lists.forEach((item) => {
item.then((res) => {
num++
resArr.push(res)
if (resArr.length === lists.length) {
resolve(resArr)
}
})
})
})
}
allSettled
allSettled 会把 Promise 成功或者失败的值都获取到
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err')
}, 2000)
})
Promise.allSettled([p1, p2]).then((res) => {
console.log(res)
})
手动实现 allSettled 方法
static allSettled(lists) {
let resArr = new Array(lists.length)
let num = 0
return new MPromise((resolve,reject) => {
lists.forEach((item, key) => {
let obj = {}
item.then(
(res) => {
obj['statue'] = 'fulfilled'
obj['val'] = res
resArr[key] = obj
num++
if (num >= lists.length) {
resolve(resArr)
}
},
(err) => {
obj['statue'] = 'rejected'
obj['val'] = err
resArr[key] = obj
num++
if (num >= lists.length) {
reject(resArr)
}
}
)
})
})
}
总结
个人觉得对 Promise 的理解可能还是没有很深入的理解,毕竟刚刚入门 希望看到这篇文章的人有什么想法能够和笔者交流,有问题的地方及时指出,共同进步!