手写一个Promise
采用ES5 IIFE格式
/**
* 自定义myPromise函数模块
*
* ES5 IIFE
*
* Isaac_Jeeen 2021_09_27
*/
(function (Window) {
const PEN = 'pending'
const RES = 'resolved'
const REJ = 'rejected'
/**
* 执行器函数
* @param {function} executor
*/
function myPromise(executor) {
const self = this
// 状态 初始值为pending
self.status = PEN
// 用于存储结果数据的属性
self.data = undefined
// 保存.then中的回调
// 数组中每个元素的结构{onResolved(){} , onRejected(){}}
self.callback = []
function resolve(value) {
// 如果状态不是pending,就不能改
if (self.status !== PEN) return
// 改状态
self.status = RES
// 保存value
self.data = value
// 如果有回调,立即异步执行 onResolved函数
if (self.callback.length > 0) {
self.callback.forEach(callbackObj => {
setTimeout(() => {
callbackObj.onResolved(value)
})
});
}
}
function reject(reason) {
// 如果状态不是pending,就不能改
if (self.status !== PEN) return
// 改状态
self.status = REJ
// 保存value
self.data = reason
// 如果有回调,立即异步执行 onResolved函数
if (self.callback.length > 0) {
self.callback.forEach(callbackObj => {
setTimeout(() => {
callbackObj.onRejected(reason)
})
});
}
}
try {
executor(resolve, reject)
}
catch (error) {
// 如果执行器抛出异常,myPromise变为失败状态
reject(error)
}
}
/**
* myPromise对象的resolve()
* 返回指定结果的成功的myPromise
*/
myPromise.resolve = function (value) {
// 返回一个新的myPromise对象,可以是成功的,也可以是失败的
// 成功的情况
// 1.1收到是是非myPromise
// 1.2收到的是成功的myPromise
// 失败的情况
// 2.1收到一个失败的myPromise
return new myPromise((resolve, reject) => {
if (value instanceof myPromise) {
// 接受的参数是myPromise
// value是传进来的myPromise
// 回调是return出去的新myPromise
// 主要就是根据传进来的对象的状态,自适应更改return出去的新myPromise状态
// 成功就resolve(),失败就reject(),参数都是上一个myPromise自动传进去的
// 见.then()的实现
value.then(resolve, reject)
}
else {
// 接受参数非myPromise
// 直接让这个返回的myPromise对象成功
resolve(value)
}
})
}
/**
* myPromise对象的reject()
* 返回指定结果的失败的myPromise
*/
myPromise.reject = function (reason) {
// 只接受reason(失败的原因),参数不可以是一个新的myPromise对象
// 返回一个新的只能是失败的myPromise对象
// 将失败的原因传递下去
return new myPromise((resolve, reject) => {
// 因为要返回一个失败的myPromise对象
// 让一个myPromise对象失败的途径就是调用它的reject()
// 同时将失败的原因reason传递进去
reject(reason)
})
}
/**
* myPromise对象的then()
* 返回一个新的myPromise对象
* @param {function} onResolved 成功的回调
* @param {function} onRejected 失败的回调
*/
myPromise.prototype.then = function (onResolved, onRejected) {
const self = this
// 指定回调函数的默认值(必须是函数)
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 返回一个新的myPromise对象
return new myPromise((resolve, reject) => {
/**
* 将重复的代码抽离、复用
* @param {function} callback
*/
function handle(callback) {
// 1.如果抛出异常,返回失败的myPromise reason是error
// 2.如果回调函数return不是myPromise对象,那么返回成功的myPromise,value就是返回值
// 3.如果回调return就是myPromise对象,那么返回的myPromise根据return的myPromise结果
try {
// 接收上一个myPromise的onResolved()【成功回调】返回的结果
const res = callback(self.data)
if (res instanceof myPromise) {
// 3.如果回调return就是myPromise对象,那么返回的myPromise根据return的myPromise结果
// 如果上一个回调返回的就是myPromise对象
// 那返回的myPromise对象根据上一个的来
// 这里的res就是上一个myPromise对象的成功回调返回的新myPromise【A】对象
// 直接调用then来获得返回的【A】的状态,以便return出一个一样的myPromise【B】
// --------------------
res.then(
// 注意!!
// 这里的resolve和reject都是调用return 出来的这个B的
// 因为要让B成功!!!
// 让一个myPromise对象成功的方法就是调用它的resolve()
// 前一个value是A(res)的成功的value 传过来的
// 后一个value是传给B的!!因为要让B成功,所以调用它的resolve(),并传入A的value
// reject同理,B按照A来
value => resolve(value),
reason => reject(reason)
)
// -----------------------
// 等价于
// -----------------------
// res.then(resolve, reject)
// ------------------------
// 解释
// 原来的value => resolve(value),
// 就是一个函数,把A的value传给B的resolve()
// 等价于直接用B的resolve当回调,且不要执行
// 因为回调函数三原则(自定义的函数,自己没有调用,最终执行)
/**
* (1)
* function fun(){}
* div.onclick = event => {fun(event)}
* 等价于
* (2)
* div.onclick = func
* 千万不要写成
* div.onclick = func()
* 这样的话就等于把func执行 return的结果给点击绑定了
*
* 回调应该是一个函数(不管是上面的箭头还是普通函数)!!!而不是执行结果
* 回调函数不用我们执行而是绑定的事物执行,回调函数三原则(自定义的函数,自己没有调用,最终执行)
* 对于(1),回调函数是箭头函数,然后等它被执行完,结果还是是一个结果[fun(event)]
* fun(event)的意思就是fun的执行结果,参数是绑定的点击事件的event
*
* 对于(2),回调函数就是fun(没执行),等它被执行的时候形参就是绑定的点击事件的event
*/
}
else {
// 2.如果回调函数return不是myPromise对象,那么返回成功的myPromise,value就是返回值
// 如果上一个的回调函数return不是myPromise对象
// 那么返回成功的myPromise,value就是返回值
// 直接调用返回的myPromise对象的resolve
// 把上一个myPromise对象的成功回调返回的结果传进去
resolve(res)
}
}
catch (error) {
// 1.如果抛出异常,返回失败的myPromise reason是error
// 因为要返回一个失败的myPromise,所以调用返回的myPromise的reject()
// 因为要失败的信息,所以error传入当前myPromise的reject(error)
reject(error)
}
}
// 如果是先指定的回调,后修改状态
// 那么status就是pengding,可以将成功失败回调函数收集起来
// 等待resolve | reject修改完状态后再调用
if (self.status === PEN) {
self.callback.push({
// 上一个myPromise状态还没确定呢
// 所以调用一下上一个myPromise对象的onResolved和onRejected函数
onResolved() {
handle(onResolved)
},
onRejected() {
handle(onRejected)
}
})
}
// 如果是修改完状态再指定回调函数
// 且状态已经是成功的(resolved)
else if (self.status === RES) {
// .then中的两个回调函数本身需要异步执行
setTimeout(() => {
handle(onResolved)
})
}
else {
// self.status === rejected
setTimeout(() => {
handle(onRejected)
})
}
})
}
/**
* myPromise对象的catch()
* 返回一个新的myPromise对象
* @param {function} onRejected 只有失败的回调
*/
myPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
/**
* myPromise对象的all()
* 返回一个myPromise (只有当所有参数全部成功时才成功)
*
*/
myPromise.all = function (myPromises) {
// 返回一个myPromise对象,所有的都成功才会返回成功的myPromise对象,value为成功值的数组
// 有一个失败就返回失败的myPromise,reason是失败的那个reason
// 凡是跟形参myPromise状态有关的,步骤如下
// 1、return new myPromise
// 2、执行器函数里面执行形参相关的.then,因为.then就是能获得成功/失败的结果并返回一个新myPromise
// 3、为了让return的新的myPromise状态与之相关,要调用return的这个myPromise的resolve/reject
const values = new Array(myPromises.length)// 用来保存所有形参中成功的myPromise的数据
let count = 0// 用来保存成功的myPromise数量
return new myPromise((resolve, reject) => {
// 遍历获取形参中所有myPromise的结果
myPromises.forEach((p, index) => {
myPromise.resolve(p).then(
value => {
values[index] = value
count++
if (count === myPromises.length) {
resolve(values)
}
},
reason => {
reject(reason)
}
)
})
})
}
/**
* myPromise对象的race()
* 返回一个myPromise,结果由第一个完成的myPromise决定
*
*/
myPromise.race = function (myPromises) {
return new myPromise((resolve, reject) => {
myPromises.forEach((p, index) => {
myPromise.resolve(p).then(
value => {
// 第一个异步完成的且成功的直接返回成功的myPromise
resolve(value)
},
reason => {
// 第一个异步完成的且失败的直接返回失败的myPromise
reject(reason)
}
)
})
})
}
// 向外暴露
window.myPromise = myPromise
})(window)