先贴上代码,后面是实现过程(这里不考虑很多的边界情况)
目录
这里是全部代码,下面是实现过程
const PROMISE_STATUS = {
PENDING: 'pending',//执行中
FULFILLED: 'fulfilled',//成功
REJECTED: 'rejected',//失败
}
/**
* 捕获异常函数
* @param {回调的方法} execFn
* @param {回调要传的值} value
* @param {成功} resolve
* @param {失败} reject
*/
function executorCatch(execFn, value, resolve, reject) {
// 如果抛出异常就执行异常
try {
const res = execFn(value)
resolve()
} catch (e) {
reject(e)
}
}
class P {
constructor(executor) {
this.status = PROMISE_STATUS.PENDING//当前状态
this.value = undefined//成功值
this.reason = undefined//失败值
this.onFulfilledFun = []//有成功回调全部push进去
this.onRejectedFun = []//有失败回调全部push进去
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.onFulfilledFun.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.onRejectedFun.forEach(fn => {
fn(this.reason)
})
})
}
}
// 假设状态为pending也抛出异常,我们可捕获一下
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
// 回调then方法
then(onFulfilled, onRejected) {
// 当只执行catch时,这个onFulfilled会变成undefined,就给他赋值value
onFulfilled = onFulfilled || (value => { return value })
onRejected = onRejected || (err => { throw new Error(err) })
return new P((resolve, reject) => {
if (this.status === PROMISE_STATUS.FULFILLED && onFulfilled) {
executorCatch(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS.REJECTED && onRejected) {
executorCatch(onFulfilled, this.reason, resolve, reject)
}
if (this.status === PROMISE_STATUS.PENDING) {
// 不是undefined的时候才push,下面同理
if (onFulfilled) this.onFulfilledFun.push(() => {
executorCatch(onFulfilled, this.value, resolve, reject)
})
if (onRejected) this.onRejectedFun.push(() => {
executorCatch(onRejected, this.reason, resolve, reject)
})
}
})
}
// 回调catch方法
catch(onRejected) {
return this.then(undefined, onRejected)
}
// 回调finally方法,这个方法就是不论成功还是失败都会执行
finally(onFinally) {
this.then(() => (onFinally()), () => { onFinally() })
}
// 静态方法
static resolve(value) {
return new P(resolve => { resolve(value) })
}
static reject(reason) {
return new P((resovle, reject) => { reject(reason) })
}
// 当全部都完成就resolve,当有未完成的就调都会调reject
static all(promises) {
// 重点,什么时候执行resolve,什么时候执行reject弄清楚
return new P((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 P(resolve => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: PROMISE_STATUS.FULFILLED, value: res })
if (results.length === promises.length) {
resolve(results)
}
}).catch(e => {
results.push({ status: PROMISE_STATUS.REJECTED, value: e })
if (results.length === promises.length) {
reject(results)
}
})
})
})
}
// 只要有结果就返回
static race(promises) {
return new P((resolve, reject) => {
promises.forEach(promise => {
// promise.then(resolve, reject)//等同于下面的代码
promise.then(res => {
resolve(res)
}), err => {
reject(err)
}
})
})
}
// 一个成功,就算成功,失败收集
static any(promises) {
const reasons = []
return new P((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reason.push(err)
if (reasons.length === promises.length) {
// 这个要用浏览器跑,我们之前一直在node下跑的
reject(new Aggregateerror(reasons))
}
})
})
})
}
}
const promise = new P((resolve, reject) => {
console.log('状态:pending');
resolve('111')
// 成功了,就并不会调用下面了
// reject('000')
})
// 回调传值
// promise.then(res => {
// console.log('res传递的值1:', res);
// throw new Error('我是异常')
// }, err => {
// console.log('err传递的值1:', err);
// }).then(res => {
// console.log('res传递的值2:', res);
// }, err => {
// console.log('err传递的值2:', err);
// })
// promise.then(res => {
// console.log('res传递的值:', res);
// }, err => {
// console.log('err传递的值:', err);
// })
// 延迟一秒执行
// setTimeout(() => {
// promise.then(res => {
// console.log('res延迟调用', res)
// }, err => {
// console.log('err延迟调用', err)
// })
// }, 1000)
// 测试catch方法
// promise.then(res => {
// }).catch(e => {
// console.log('catch抛出的异常', e);
// })
// 测试finally方法
// promise.then(res => {
// console.log('res传值', res);
// }).catch(e => {
// console.log('catch抛出的异常', e);
// }).finally(() => {
// console.log('finally');
// })
// 直接调用
// P.resolve('成功了').then(res => {
// console.log('直接调用res', res);
// })
// P.reject('出错了').catch(e => {
// console.log('直接调用e', e);
// })
// 模拟调用
const p1 = new P((resolve) => {
setTimeout(() => { resolve(111) }, 1000)
})
// const p2 = new P((resolve, reject) => {
// setTimeout(() => { reject(222) }, 2000)
// })
const p2 = new P((resolve) => {
setTimeout(() => { resolve(222) }, 2000)
})
const p3 = new P((resolve) => {
setTimeout(() => { resolve(333) }, 3000)
})
// P.all([p1, p2, p3]).then(res => {
// console.log(res);
// }).catch(e => {
// console.log(e);
// })
// P.allSettled([p1, p2, p3]).then(res => {
// console.log(res);
// })
P.race([p1, p2, p3]).then(res => {
console.log(res);
}).catch(e => {
console.log(e);
})
一、基本调用
主要思路:
1. 创建一个对象,传入resolve和reject,在类中执行构造函数(每new一个对象就会执行)
2. 在构造函数中定义了两个常量放进executor方法里,然后两个常量在新建的promise中调用
这里留下的问题?我们知道promise在调用后resolve后就不会再执行reject了,我们第二点解决
二、promise状态
主要思路:
1. 定义三个常量,在构造函数中定义状态(this.status)绑定默认值pending
2. 调用resolve时,把fulfilled赋值于当前状态,就只会调用一个方法
这里留下的问题?我们怎么传递参数呢?下一点说
三、传递参数
主要思路:
1.在p类中,定义成功值和失败值,当成功时,传递过来的形参赋值this.value
当失败时,传递过来的形参赋值this.reason
2. 在p类中,定义then方法,传递两个形参命名为onFulfilled, onRejected(开发规范,尽量这样命名),然后看52行,新建的promise对象点then方法,传递两个函数,最后看17和28行,在这里调用then方法中赋值的方法
3. 使用queueMicrotask方法
queueMicrotask() - Web APIs | MDN
执行完全部代码后,再回来执行queueMicrotask(),这样就避免报错问题,看47行
(假设把this.status放到queueMicrotask()里面,会出现什么情况?即43行执行完了,还会接着执行45行的内容)
这里留下的问题?
问题1:当52-57复制一份,会执行两次吗?答案是不会的,因为在36行进行覆盖了,后面我们来解决
问题2:我们的回调传值的执行promise.then()方法还能不能接着点then()呢?
四、完善then()方法
这里有点难呀,但是这个then()写好了,后面方法都是基于这个then()的,坚持看完呀,不要慌,把这里看懂了
主要思路:
解决第三点留下的问题1,复制一份回调传值,然后在p类中定义两个数组,每次调用then()方法,就把一次回调push到这个数组,最后遍历数组执行
如果我们再写一个延迟一秒再执行,会发现我们调用不了,为什么呢?
因为在新建的promise对象中,我们已经执行完了resolve(),那么过一秒后再执行promise.then()就会为空,怎么解决呢?
setTimeout(() => {promise.then(res => {console.log('res延迟调用', res)}, err => {console.log('err延迟调用', err)})}, 1000)
主要思路:
1. 当新建的promise对象执行完后,状态为fulfilled/rejected的时候,就可以在then中直接执行
2. 再把状态赋值放回微任务里,然后判断状态是不是pending不是的话,就不执行下面代码
解决第三点留下的问题2,我们知道promise在执行完resolve()/reject()后如果是返回的是普通数据,那就会返回一个promise,那么就可以接着用then()回调,下面我们先写一个链式调用then()
(仔细看代码行数,下面三页是对得上的,顺序换了一下而已)
然后每次调用then()的时候,直接return一个新的promise对象,然后封装一个捕获函数,即当抛出异常就调用reject(),代码如下
可以自己测试一下,当91行注释打开后
五、编写catch()方法
主要思路:
1. 编写catch方法,传入异常,调用then(),一参为undefined,二参异常
2. 当执行then()时,一参就是第一条的undefined,不传入第二个参数,即reject为undefined
3. 当then()二参为undefined时,抛出异常
(63行之前的代码不变)
六、编写finally()方法
主要思路:
1. 定义一个finally方法,然后调用then()方法,然后把finally()的形参都放到then()形参一和形参二中
(因为不论执行reslove()还是执行reject()都会调用这个方法)
2. 回调方法做一些处理,然后测试代码
(别的代码不变)
七、实现resolve()和reject()类方法
在p类中新增,然后测试代码(其他代码不变)
八、实现all()和allSettled()类方法
在p类中新增,然后测试代码(其他代码不变)
九、实现race()和any()类方法
在p类中新增,然后自己测试吧