//本文带大家实现一个基本的 promise 过多的边界情况就不在考虐,理解主要实现过程以及逻辑即可
//对于一个个出现的问题 我会逐步分析原因以及对应的解决思路
//前提是你必须掌握 promise的基本使用,以及回调函数有对应的理解
//第一步:初步实现一个最简单的Promise (循序渐进的方式往下编写,小白也能听懂,当然大神可直接跳过一些废话~~~ )
一:首先分析官方的promise
//---首先分析官方的promise
const p1 = new Promise((resolve, reject) => {
resolve(2222)
reject(33333)
})
// 调用then方法 then 方法接收2个回调函数作为参数
p1.then(res => { //只会打印222, 因为promise的状态一但由pending的状态改变了 就无法再次改变
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
//解析:
//由此可见 promise 的参数 接收一个函数,在函数中又接收了2个回调函数 作为参数,然后调用resolve 方法
//在执行then方法。 then 函数又接收2个参数 均为回调函数,当执行 resolve(2222)后 就会来到then 的第一个参数 的回调函数
// 执行 reject(33333)后 就会来到then 的第二个参数回调函数-
//1.那么初步编写可得:
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING //1.初始化状态 pending
this.value = undefined //2.保存参数
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) { //3. 只有pending状态才可改变
this.status = PROMISE_STATUS_FULFILLED
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
}
})
executor(resolve, reject) //在new myPromise 时初始化立即调用传递过来的函数
}
}
二:手写Promise-then方法设计
//2. 执行resolve 或者 reject 会调用then 方法(在上面的代码基础改动)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.resfn(value)
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.errfn(error)
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//(看代码是不是觉得最基本的实现了)接下来我们执行下自己实现的promise,看是否正常
const p1 = new myPromise((resolve, reject) => {
resolve(111)
})
p1.then(res => {
console.log(res);
}, err => {
})
//执行后你会发现报错了 错误信息: this.resfn is not a function
//那么为什么会报错呢? 因为和 代码的执行顺序有关 当我们 new myPromise()执行resolve 的时候就会来到resolve方法
//而你的then 方法鸭羹就没有执行到呢 所以就报错了
//解决方法: 只要让then 先执行就可以 , 将resolve 或者 reject 要执行then 方法时添加到异步队列即可
上代码:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
//大家可能会想到使用setTimeout 然后0秒执行,但此处使用queueMicrotask 会更加合适
// setTimeout(() => {
// this.resfn(value)
// }, 0);
queueMicrotask(() => { //queueMicrotask: 主线程执行完毕之后立马执行
this.resfn(value)
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.errfn(error)
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//举一个栗子:
setTimeout(() => {
console.log('setTimeout');
}, 0);
queueMicrotask(() => {
console.log('queueMicrotask');
});
//实际运行
queueMicrotask
setTimeout
然后执行:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
reject(333333)
})
p1.then(res => { //最终打印 1111
console.log(res);
}, err => {
console.log(err);
})
到这里 我们最简单的实现就以经完成了,接下来我们继续 优化then 方法:
优化一:
(官方的Promise 的then是可以多次调用的)
// 我们自己实现的mypromise 调用then方法多次时 我们目前是不支持的
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
// 调用then方法多次调用
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
运行结果:res2: 111 因为后面的.then 把前面的覆盖掉了 并不会执行res1 所在的代码块
*由此可见 then 方法调用时应该是个数组然后依次调用
下面我们改造我们的代码:
上代码:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
queueMicrotask(() => {
this.value = value
this.resfns.forEach(fn => { //循环依次调用
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //将多次调用的then 方法保存到数组中,依次调用
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
然后执行:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
reject(333333)
})
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
// 调用then方法多次调用
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
执行结果:
res1: 111
res2: 111
这样是不是就解决了 我们多次调用的问题, 那么仔细看我们的代码
我们在 resolve 函数 中 只有他的状态为pending的时候 我们才会执行 then的回调函数
那么如果我们执行then 的时候 他的状态 已经不是pending的时候 那么是不是就不会执行了呢?
那有些朋友可能会问到了:“哎,怎么可能执行then 的时候他的状态 已经被改变了呢 我明明看到你写的代码 ,是把它成功的回调和失败的回调push在了数组里面,然后依次的执行吗”
说的可能比较抽象:接下来我们用代码复现这个问题
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111)
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)
})
/** 仔细看这里 上面的代码是同步的 执行会被添加到对应的数组里面依次调用,调用完毕后,他的状态是不是就已经确定好了 */
//然后你再执行下面的这个异步代码 那这个时候肯定是啥都不会执行了 因为你的状态已经被改变了,此时你已经 this.resfns.push(resfn)了这个函数,但是他压根就不会执行了
setTimeout(() => {
promise.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
执行结果:
res1: 111
res2: 111
由此可见 第三次的then 并没有执行
解决思路:
如果在then调用的时候, 状态已经确定下来 那么就应该直接执行then 里面回调即可
上代码:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.如果在then调用的时候, 状态已经确定下来,那么就可以直接调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
resfn(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
errfn(this.error)
}
//2. 相反 对与已经确定的状态 就不需要在push 执行了
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
}
执行:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
// reject(333333)
})
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
// 调用then方法多次调用
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
// 在确定Promise状态之后, 再次调用then
setTimeout(() => {
p1.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
结果:
res1: 111
res2: 111
res3: 111
三:then方法优化:
对于我们之前 的then方法多次调用是通过实例化出来的类 调用的
例如:
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
这样子调用以我们目前的代码是可以实现的,但是如果是 一直链式调用下去呢?废话少说上代码:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
reject(333333)
})
p1.then(res => {
console.log("第一次的成功回调:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
现在我们代码肯定是走不通的(可以拿去自己的环境测试下)接下来继续优化我们的代码:
优化之前首先要分析一波: (已官方的promise链式调用为示例来解释)
执行resolve
调用then 方法,then方法返回的值 实则是 new myPromse((resolve,reject) => resolve("第二次的成功回调")),将返回值 用resolve()方法再传递下去
所以在打印完 "第一次的成功回调:",111 的时候 再次打印 "res2:",第二次的成功回调,
- 那么什么时候才会打印 "err2:"第二次的失败回调 呢?只需要在第一次调用then 的时候 抛出异常 就会执行err2 了
例如:
p1.then(res => {
console.log("第一次的成功回调:", res)
throw new Error("err message")
}, err => {
console.log("err1:", err)
return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
执行结果:
"第一次的成功回调:", 111
"err2: Error: err message
2.如果是执行reject ,在reject的第二个回调中
err => {
console.log("err1:", err)
return "第二次的失败回调"
}
return 了值那么接下来是 会执行 console.log("res2:", res) 还是 console.log("err2:", err) 呢?
例如:
const p1 = new myPromise((resolve, reject) => {
reject(333333)
})
p1.then(res => {
console.log("第一次的成功回调:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
这里我先告诉你 其实会执行 console.log("res2:", res) ,如果想执行 console.log("err2:", err) 锁在的代码块,需要将 return "第二次的失败回调" 改为抛出异常 才会执行
栗子:
p1.then(res => {
console.log("第一次的成功回调:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
逻辑已经理清楚 接下来用代码来表达:(可以复制下面这坨代码,然后用上面说的案例来自己测试 就比较清楚了)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
})
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
})
}
}
//然后测试:
const p1 = new myPromise((resolve, reject) => {
// resolve(111)
reject(333333)
})
p1.then(res => {
console.log("res1:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
throw new Error("err message")
// return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
结果:
err1: 333333
err2: Error: err message
OK 完美解决
四:Promise-catch方法设计
以上面的代码 我们已经实现了一个基本的promise 了
此时设置catch 方法 也就非常的简单了
上代码:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.难点:利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
return new myPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
}
//接着测试代码:
const p1 = new myPromise((resolve, reject) => {
reject(333333)
})
p1.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//结果:
err: 333333
这里涉及到一个难点就是 利用第一次的 p1.then的回调只传了一个参数的时候,给另外一个参数 赋值默认的函数
且这个函数必须抛出异常才能让 标记的tips那块代码 的catch 捕获到,然后 reject(err) 才能使用.catch
当然如果你想这么调用
p1.catch(err => {
console.log("err:", err)
}).then(res => {
console.log("res:", res)
})
也只需要再赋值默认函数 让 try里面代码块 resolve(value) 接收
只需要在第一行注释里面 再加上这句代码即可
const defaultOnFulfilled = value => {
return value
}
resfn = resfn || defaultOnFulfilled
五:Promise-finally方法设计
//finally 函数无论成功或者失败 都会调用的函数 并且是在最后调用
//直接在类上面加个finally 方法即可 利用 queueMicrotask 最快处理微任务的特性 使finally(fn) 传递过来的函数fn 最后执行
finally(fn) {
setTimeout(() => {
fn()
}, 0)
}
最后附上全部代码:
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.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
const defaultOnFulfilled = value => {
return value
}
resfn = resfn || defaultOnFulfilled
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
finally(fn) {
setTimeout(() => {
fn()
}, 0)
}
}
剩下的都是些类方法 比较简单 这里就先不讲了~~拜拜~
版权归原作者 星星周z 所有, 如有侵权,请联系我们删除。