主要由基础实现和静态类的实现两部分组成。
1 基础实现(不含静态类)
1.1 使用类实现完成构造函数
实现代码如下,构造函数传入一个回调函数,定义resolve和reject函数,将两个函数作为参数执行回调函数。
javascript">// 1. 使用类实现,完成构造函数
class MyGoPromise{constructor(callback){const resolve = (result) => {console.log(result)}const reject = (result) => {console.log(result)}callback(resolve, reject)}
}
测试代码如下:
javascript">const p = new MyGoPromise((resolve, reject) => {resolve("success")// reject("fail")
})
1.2 实现状态(pending fulfilled rejected)及结果
首先状态在类内直接声明默认值,状态固定为三个,声明为类外的字符串常量使用。实现在reject和resolve调用时候的状态变化(pending -> fulfilled, pending --> rejected)以及结果保存。代码实现如下所示:
javascript">// 2. 状态及原因 state(pending fulfilled rejected) result
// 常量最好单独定义,方便维护
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
class MyGoPromise{state = PENDINGresult = undefinedconstructor(callback){const resolve = (result) => {// 状态只能从pending变为fulfilled或者rejectedif(this.state === PENDING){this.state = FULFILLEDthis.result = result}}const reject = (result) => {if(this.state === PENDING){this.state = REJECTEDthis.result = result}}callback(resolve, reject)}
}
测试代码如下:
javascript">const p = new MyGoPromise((resolve, reject) => {resolve("success")// reject("fail")
})
console.log(p)
1.3 then的基础实现
1.3.1 then的成功以及失败回调实现(同步)
then接收两个回调函数,成功回调以及失败的回调,通过查阅文档得知传入非函数时,成功返回为x => x,失败返回为 x => throw x。然后根据状态执行相应的回调函数。then实现的代码如下:
javascript">then(onFulFilled, onRejected){onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}if(this.state === FULFILLED){onFulFilled(this.result)}else if(this.state === REJECTED){onRejected(this.result)}}
测试代码如下所示:
javascript">const p = new MyGoPromise((resolve, reject) => {// resolve("success")reject("fail")
})p.then(res => {console.log(res)}, err => {console.log(err)})
console.log(p)
1.3.2 then的异步支持以及多次调用
之前的then执行的都是同步任务,需要加入对如setTimeout等异步执行的支持,修改then函数,增加判断条件pending,成立将回调暂存到隐私函数handlers中,等到rejecet或resolve时再执行,修改的代码如下(包含then和reject和resolve的调用逻辑的修改):
javascript">constructor(callback){const resolve = (result) => {// 状态只能从pending变为fulfilled或者rejectedif(this.state === PENDING){this.state = FULFILLEDthis.result = resultthis.#handlers.forEach(({onFulFilled}) => {onFulFilled(this.result)})}}const reject = (result) => {if(this.state === PENDING){this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({onRejected}) => {onRejected(this.result)})}}callback(resolve, reject)}then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}if(this.state === FULFILLED){onFulFilled(this.result)}else if(this.state === REJECTED){onRejected(this.result)}else{this.#handlers.push({onFulFilled,onRejected})}}
}
1.4 异步任务
当我们使用下述代码测试我们已经实现的内容时会发现输出的顺序错误,代码如下所示:
javascript">// !!! 输出顺序有误 原生Promise输出为 start end success
// !!! 自定义Promise输出为 start success end
// 所以需要引入异步任务 选用核心API queueMicrotask MutationObserver setTimeout
console.log("start")
const p = new MyGoPromise((resolve, reject) => {resolve("success")
})
p.then(res => console.log(res))
console.log("end")
1.4.1 异步主要API
1. queueMicrotask函数直接传入回调函数即可执行
javascript">console.log("start")
queueMicrotask(()=>{console.log("queueMicrotask")
})
console.log("end")
2. MutationObserver notice:only works in browser 子节点改变就会执行
javascript">console.log("start")
const obs = new MutationObserver(()=>{console.log("MutationObserver")
})const divNode = document.createElement("div")
obs.observe(divNode, {childList: true})
divNode.innerHTML = "MyGo!!!!!"
console.log("end")
3. setTimeout(callback, 0)
1.4.2 异步任务实现
将异步任务执行封装成全局函数调用,用于判断以上三种异步实现浏览器是否支持,实现的runAsyncTask函数如下所示:
javascript">function runAsyncTask(callback){if(typeof queueMicrotask === "function"){queueMicrotask(callback)}else if(typeof MutationObserver === "function"){const obs = new MutationObserver(callback)const divNode = document.createElement("div")obs.observe(divNode, {childList: true})divNode.innerHTML = "MyGo!!!!!"}else{setTimeout(callback, 0)}
}
修改then函数逻辑,将回调函数封装成异步任务执行,代码如下:
javascript">then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{onFulFilled(this.result)}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{onRejected(this.result)})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{onFulFilled(this.result)})},onRejected: () =>{// 异步任务runAsyncTask(() => {onRejected(this.result)})}})}}
1.5 链式编程
链式编程实现then的链式调用,错误处理,返回值处理(通常分为返回Promise 正常值 多重引用)。
1.5.1 处理异常和普通返回值(fulfilled state)
首先实现链式调用,使用new再then函数中创建一个全新的Promise对象,将对象返回,将之前then函数的执行逻辑放入新的Promise的回调函数当中(因为会立即执行),单独处理fulfilled分支使用try...catch...捕获错误,使用reject返回错误,获取回调函数返回值,然后resolve,代码如下:
javascript"> then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}// 在newPromise时传入的回调函数会立即执行const newPromise = new MyGoPromise((resolve, reject) => {if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolve(x)} catch (error) {reject(error) }}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{onRejected(this.result)})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{onFulFilled(this.result)})},onRejected: () =>{// 异步任务runAsyncTask(() => {onRejected(this.result)})}})}})return newPromise}
测试代码如下:
javascript">const p = new MyGoPromise((resolve, reject) => {resolve(1)
})p.then(res =>{console.log(res)// throw "error layer1"return 100
}).then(res =>{console.log(res + " sub")
}, err =>{console.log(err + "sub")
})
1.5.2 处理返回Promise对象 (fulfilled state)
使用instance of判断,promise对象调用then方法处理,代码如下所示:
javascript"> then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}// 在newPromise时传入的回调函数会立即执行const newPromise = new MyGoPromise((resolve, reject) => {if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)// 在这里处理Promise对象根据结果提交if(x instanceof MyGoPromise){x.then(res =>{resolve(res)}, err =>{reject(err)})}else{resolve(x)}} catch (error) {reject(error) }}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{onRejected(this.result)})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{onFulFilled(this.result)})},onRejected: () =>{// 异步任务runAsyncTask(() => {onRejected(this.result)})}})}})return newPromise}
}
测试代码如下:
javascript">// 测试代码 -- 返回Promise对象
const p = new MyGoPromise((resolve, reject) => {resolve(1)
})p.then(res =>{return new MyGoPromise((resolve, reject) => {// resolve(2)reject("error layer2")})
}).then(res =>{// 要拿到上一个Promise resolve的结果console.log(res + " sub")
}, err =>{console.log(err + "sub")
})
1.5.3 处理重复引用 (fulfilled state)
只需要判断我们取到的回调函数的返回值和创建的新的promise对象是否一致即可,代码(包含测试代码)如下所示:
javascript">if(x === newPromise)
{throw new TypeError("Chaining cycle detected for promise")
}// test code
const p = new MyGoPromise((resolve, reject) => {resolve(1)
})const p2 = p.then((res)=>{return p2
})
1.5.4 处理rejected state 封装函数
遵循同一套逻辑,首先进行错误处理,然后判断重复引用和回调结果是否是Promise对象,所以封装函数如下:
javascript">function resolvePromise(newPromise, x, resolve, reject){if(x === newPromise){throw new TypeError("Chaining cycle detected for promise")}if(x instanceof MyGoPromise){x.then(res =>{resolve(res)}, err =>{reject(err)})}else{resolve(x)}
}
使用封装函数,代码如下:
javascript"> then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}// 在newPromise时传入的回调函数会立即执行const newPromise = new MyGoPromise((resolve, reject) => {if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error) }}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{try {const x = onRejected(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{onFulFilled(this.result)})},onRejected: () =>{// 异步任务runAsyncTask(() => {onRejected(this.result)})}})}})return newPromise}
测试代码如下:
javascript">// 测试代码
const p = new MyGoPromise((resolve, reject) => {reject(1)
})const p2 = p.then(undefined, res =>{// return 2// return p2throw "error"// return new MyGoPromise((resolve, reject) => {// resolve(3)// })
})p2.then((res)=>{console.log("p2-res: ", res)
}, err =>(console.log("p2-err: ", err)
))
1.5.5 处理pending state 调用函数
和处理fulfilled和rejected状态一样,代码如下所示:
javascript"> then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}// 在newPromise时传入的回调函数会立即执行const newPromise = new MyGoPromise((resolve, reject) => {if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error) }}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{try {const x = onRejected(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})},onRejected: () =>{// 异步任务runAsyncTask(() => {try {const x = onRejected(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})}})}})return newPromise}
测试代码如下:
javascript">// 测试代码
const p = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success")}, 2000)
})const p2 = p.then(res =>{// return 2// return p2throw "error"// return new MyGoPromise((resolve, reject) => {// resolve(3)// })
})p2.then((res)=>{console.log("p2-res: ", res)
}, err =>(console.log("p2-err: ", err)
))
1.6 实现catch方法
处理错误,相当于调用then方法,但是还要处理new Promise回调里面的函数错误,修改部分代码如下:
javascript">
//新增catch函数
catch(onRejected){// 相当于变相调用then方法return this.then(undefined, onRejected)
}// 处理实例化时的异常try {callback(resolve, reject)} catch (error) {// 处理实例化时的异常reject(error)}
测试的代码如下所示:
javascript">// 测试代码
const p = new MyGoPromise((resolve, reject) => {throw "error"// reject("reject-error")
})p.then((res)=>{console.log("p-res: ", res)
}).catch(err =>(console.log("p-catch-err: ", err)
))
1.7 finally实现
不管是什么状态,最终都要执行的回调函数,也是调用then方法,只是不管是成功回调还是失败回调都是同一个回调函数,实现代码如下:
javascript">finally(onFinally){return this.then(onFinally, onFinally)
}
测试代码如下:
javascript">const p = new MyGoPromise((resolve, reject) => {throw "error"// reject("reject-error")
})p.then((res)=>{console.log("p-res: ", res)
}).catch(err =>(console.log("p-catch-err: ", err)
)).finally(()=>{console.log("finally")
})
2 静态类方法实现
2.1 resolve方法实现
调用该方法将传入的值(非Promise)转换为fulfilled状态的Promise返回,如果传入Promise直接返回。
javascript">static resolve(value){if(value instanceof MyGoPromise){return value}return new MyGoPromise((resolve) => {resolve(value)})
}
测试代码如下:
javascript">// 测试代码
const p = MyGoPromise.resolve(new MyGoPromise((resolve, reject) => {// resolve("success")reject("reject-error")})
).then(res =>{console.log("res: ", res)
}, err =>{console.log("err: ", err)
})
2.2 reject方法实现
不管传入什么值,直接将返回值作为原因返回rejected状态的promise,实现的代码如下:
javascript">static reject(value){return new MyGoPromise((resolve, reject) => {reject(value)})
}
测试代码如下:
javascript">// 测试代码
MyGoPromise.reject("reject-error").catch(err =>{console.log("err: ", err)
})
2.3 race方法实现
race接收一个数组作为参数,返回最先完成(fulfilled)的promise对象,如果传入不是数组则拨错,实现代码如下:
javascript">static race(promises){return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.forEach(promise =>{MyGoPromise.resolve(promise).then(res => {resolve(res)}, err =>{reject(err)})})})
}
测试代码如下:
javascript">// 测试代码
const p1 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success")}, 2000)
})const p2 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{reject("reject-error")}, 1000)
})MyGoPromise.race([p1, p2]).then(res =>{console.log("res: ", res)
}, err =>{console.log("err: ", err)
})
2.4 all方法实现
也是传入一个数组,有一个错误就reject,全部成功返回一个数组(按顺序),如果传入空数组直接返回空数组结果,实现代码如下:
javascript">static all(promises){// 1.返回Promise实例// 2.判断是否时数组// 3. 判断是否为空数组,空数组直接兑现// 4. 处理全部兑现 v,k 存储数据, count计数判断是否拒绝// 5. 处理拒绝 直接拒绝即可return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && resolve([])let count = 0const result = []promises.forEach((promise, index) => {MyGoPromise.resolve(promise).then(res =>{result[index] = rescount ++count === promises.length && resolve(result)}, err =>{reject(err)})})})
}
测试代码如下所示:
javascript">// 测试代码
const p1 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success 1")}, 2000)
})const p2 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success 2")}, 1000)
})MyGoPromise.all([p1, p2]).then(res =>{console.log("res: ", res)
}, err =>{console.log("err: ", err)
})
2.5 allsettled方法实现
无论成功还是失败,都是返回一个对象数组,数组中存储state和value以及reason,基本照搬all方法的实现,代码如下:
javascript">static allsettled(promises){return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && resolve([])let count = 0const result = []promises.forEach((promise, index) => {MyGoPromise.resolve(promise).then(res =>{result[index] = {value: res,status: "fulfilled",}count ++count === promises.length && resolve(result)}, err =>{result[index] = {reason: err,status: "rejected"}count ++count === promises.length && resolve(result)})})})
}
测试代码如下所示:
javascript">// 测试代码
const p1 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success 1")}, 2000)
})const p2 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{reject("success 2")}, 1000)
})MyGoPromise.allsettled([1, p1, p2]).then(res =>{// 会打印成功和失败的结果console.log("res: ", res)
}, err =>{console.log("err: ", err)
})
2.6 any方法实现
任意一个成功就返回成功,全部失败才返回失败,和all反着来,但是在any方法中,如果传入空数组也会有aggregate的报错,是新代码如下:
javascript">static any(promises){return new MyGoPromise((resolve, reject)=>{if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && reject(new AggregateError([]))let count = 0const errors = []promises.forEach((promise, index)=>{MyGoPromise.resolve(promise).then(res => {resolve(res)}, err => {errors[index] = errcount ++count === promises.length && reject(new AggregateError(errors))})})})
}
测试代码如下所示:
javascript">// 测试代码
const p1 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{resolve("success 1")}, 2000)
})const p2 = new MyGoPromise((resolve, reject) => {setTimeout(()=>{reject("success 2")}, 1000)
})MyGoPromise.any([p1, p2, MyGoPromise.reject(1)]).then(res =>{// 会打印第一个成功的结果 1console.log("res: ", res)
}, err =>{console.log("err: ", err)
})
3 完整代码展示
javascript">function runAsyncTask(callback){if(typeof queueMicrotask === "function"){queueMicrotask(callback)}else if(typeof MutationObserver === "function"){const obs = new MutationObserver(callback)const divNode = document.createElement("div")obs.observe(divNode, {childList: true})divNode.innerHTML = "MyGo!!!!!"}else{setTimeout(callback, 0)}
}function resolvePromise(newPromise, x, resolve, reject){if(x === newPromise){throw new TypeError("Chaining cycle detected for promise")}if(x instanceof MyGoPromise){x.then(res =>{resolve(res)}, err =>{reject(err)})}else{resolve(x)}
}const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
class MyGoPromise{state = PENDINGresult = undefined// 使用对象数组保存pending时候的失败调用和成功调用#handlers = []constructor(callback){const resolve = (result) => {// 状态只能从pending变为fulfilled或者rejectedif(this.state === PENDING){this.state = FULFILLEDthis.result = resultthis.#handlers.forEach(({onFulFilled}) => {onFulFilled(this.result)})}}const reject = (result) => {if(this.state === PENDING){this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({onRejected}) => {onRejected(this.result)})}}try {callback(resolve, reject)} catch (error) {// 处理实例化时的异常reject(error)}}then(onFulFilled, onRejected){// 当传入的值不是函数时,需要按照以下两种形式返回onFulFilled = typeof onFulFilled === "function" ? onFulFilled : x => xonRejected = typeof onRejected === "function" ? onRejected : e => {throw e}// 在newPromise时传入的回调函数会立即执行const newPromise = new MyGoPromise((resolve, reject) => {if(this.state === FULFILLED){// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error) }}) }else if(this.state === REJECTED){// 异步任务runAsyncTask(()=>{try {const x = onRejected(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})}else{this.#handlers.push({onFulFilled:() => {// 异步任务runAsyncTask(()=>{try {const x = onFulFilled(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})},onRejected: () =>{// 异步任务runAsyncTask(() => {try {const x = onRejected(this.result)resolvePromise(newPromise, x, resolve, reject)} catch (error) {reject(error)}})}})}})return newPromise}catch(onRejected){// 相当于变相调用then方法return this.then(undefined, onRejected)}finally(onFinally){return this.then(onFinally, onFinally)}static resolve(value){if(value instanceof MyGoPromise){return value}return new MyGoPromise((resolve) => {resolve(value)})}static reject(value){return new MyGoPromise((resolve, reject) => {reject(value)})}static race(promises){return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.forEach(promise =>{MyGoPromise.resolve(promise).then(res => {resolve(res)}, err =>{reject(err)})})})}static all(promises){// 1.返回Promise实例// 2.判断是否时数组// 3. 判断是否为空数组,空数组直接兑现// 4. 处理全部兑现 v,k 存储数据, count计数判断是否拒绝// 5. 处理拒绝 直接拒绝即可return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && resolve([])let count = 0const result = []promises.forEach((promise, index) => {MyGoPromise.resolve(promise).then(res =>{result[index] = rescount ++count === promises.length && resolve(result)}, err =>{reject(err)})})}) }static allsettled(promises){return new MyGoPromise((resolve, reject) => {if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && resolve([])let count = 0const result = []promises.forEach((promise, index) => {MyGoPromise.resolve(promise).then(res =>{result[index] = {value: res,status: "fulfilled",}count ++count === promises.length && resolve(result)}, err =>{result[index] = {reason: err,status: "rejected"}count ++count === promises.length && resolve(result)})})}) }static any(promises){return new MyGoPromise((resolve, reject)=>{if(!Array.isArray(promises)){reject(new TypeError("Argument is not iterable"))}promises.length === 0 && reject(new AggregateError([]))let count = 0const errors = []promises.forEach((promise, index)=>{MyGoPromise.resolve(promise).then(res => {resolve(res)}, err => {errors[index] = errcount ++count === promises.length && reject(new AggregateError(errors))})})})}}