手动实现一个Promise-----XPromise
代码有注释,前面部分可以按顺序一部分一部分的看,循序渐进,可以试着敲一敲,感觉也很好理解,我也是学习来的,欢迎大家共同探讨(虽然这个没啥可探讨的~~)
const _promise=new XPromise((resolve,reject)=>{
resolve('成功')
reject('失败')
})
_promise.then(res=>{
console.log('then',res);
}).catch(err=>{
console.log('catch',err);
}).finally(()=>{
console.log('finally');
})
主要有二个大模块
- 核心功能
- 方法
- 外加测试
核心功能
通过new去实例化一个promise对象,里面传入一个回调函数可以执行异步操作,成功通过resolve传入成功的值,失败通过reject传入失败的值...算了,话不多说,直接开撸吧,用过promise的你应该都清楚一些基本介绍。
进入正题
将从以下五个模块逐步推进,按序号步骤看
graph LR
构造函数 --> 状态及原因 --> then实例方法-->异步任务-->链式编程
-
- 构造函数
- 1.1 定义一个XPromise类(自定义)
- 1.2 类里面整一个构造函数,接收传来的参数,参数包含了resolve,reject方法
- 1.3 构造函数内部定义resolve和reject方法
- 1.4 执行回调函数
//1.1定义XPromise类 class XPromise{ // 1.2添加构造函数 constructor(callback){ // 1.3定义resolve/reject方法 const resolve=(res)=>{ console.log(res);//成功 } const reject=(error)=>{ console.log(error);//失败 } // 1.4执行回调函数 callback(resolve,reject) } } const _promise=new XPromise((resolve,reject)=>{ //resolve('成功') //reject('失败') })
这里你就完成了基础搭建
状态和原因
-
- 按步骤操作
- 2.1 定义全局状态PENDING
- 2.2 添加status(记录状态)、reason(记录原因)
- 2.3 添加状态判断(一旦改变就不可再改变, 如果不加判断,下面同时执行,reject会覆盖resolve,可尝试下)
- 2.4 设置状态和原因
// 2.1 定义全局状态 const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected"; class XPromise{ // 2.2 用status实例属性设置状态 status=PENDING //默认状态是pending // 2.2 用reason实例属性来设置原因 reason=undefined //初始原因是undefined constructor(callback) { const resolve = (res) => { //2.3 这里添加判断 让状态不可逆 if (this.status === PENDING) { // 2.4 成功是fulfilled 状态 this.status = FULFILLED // 2.4 记录成功原因 this.reason = res } } const reject = (error) => { //2.3 这里添加判断 让状态不可逆 if (this.status === PENDING) { // 2.4 失败是rejected 状态 this.status = REJECTED // 2.4 记录失败原因 this.reason = error } } callback(resolve, reject) } const _promise=new XPromise((resolve,reject)=>{ console.log('第一步执行了'); resolve('成功') // reject('失败') })
then方法
- 先看看then的用法
-
- 写一个then
-
3.1 添加实例化then方法
-
3.2 判断传入的是否是函数类型,如果是就执行成功和失败的回调,如果不是就根据MDN文档developer.mozilla.org/zh-CN/docs/…
-
3.3 根据不同的状态执行回调,并传入原因
const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected"; class XPromise{ status=PENDING //默认状态是pending reason=undefined //初识原因是undefined constructor(callback) { const resolve = (res) => { if (this.status === PENDING) { this.status = FULFILLED this.reason = res } } const reject = (error) => { if (this.status === PENDING) { this.status = REJECTED this.reason = error } } // 执行回调函数 callback(resolve, reject) } //3.1 添加实例化then方法 //onFulfilled,onRejected 对应的其实就是then后面的res和error,参考上面then方法的图 then(onFulfilled,onRejected){ //3.2 这里首先得判断传入的是不是一个函数 如果是函数就执行 不是函数 就参考的MDN文档直接返回 onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x //如果把下面then里面的error去掉,就不是函数,就会报错,调用reject就会输出'失败',因为这里throw抛出异常了 onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; } //3.3 .根据不同的状态执行不同的方法,返回不同的值 if(this.status===FULFILLED){ // 成功状态 执行成功函数 并且传入执行原因 onFulfilled(this.reason) }else if(this.status===REJECTED){ // 失败状态 执行失败函数 并且传入执行原因 onRejected(this.reason) } } } const _promise = new XPromise((resolve, reject) => { console.log('第一步执行了'); // resolve('成功') // reject('失败') }) _promise.then(res => { console.log('resolve进入', res); }, error => { console.log('reject进入', error); })
-
- 让then支持异步和多次调用
-
如果在setTimeout中执行,下面的then是不会输出的
-
4.1 定义一个私有属性(#表示私有) 用来存储回调函数
-
4.2 在PENDING状态将回调函数存储起来
-
4.3 在执行成功和失败的方法里面将存储的回调函数取出来执行
const _promise = new XPromise((resolve, reject) => { // console.log('第一步执行了'); setTimeout(() => { resolve('成功') }, 2000); // reject('失败') }) _promise.then(res => { console.log('resolve1进入', res); }, error => { console.log('reject1进入', error); }) _promise.then(res => { console.log('resolve2进入', res); }, error => { console.log('reject2进入', error); })
-
实现支持异步调用并支持多次then调用
const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected"; class XPromise{ status=PENDING //默认状态是pending reason=undefined //初识原因是undefined // 4.1 定义一个私有属性 用来存储 成功和失败的方法 #method=[] //[{onFulfilled,onRejected}...] constructor(callback) { const resolve = (res) => { if (this.status === PENDING) { this.status = FULFILLED this.reason = res //4.3 存储的取出来挨个执行 this.#method.forEach(({onFulfilled})=>onFulfilled(this.reason)) } } const reject = (error) => { if (this.status === PENDING) { this.status = REJECTED this.reason = error //4.3 存储的取出来挨个执行 this.#method.forEach(({onRejected})=>onRejected(this.reason)) } } callback(resolve, reject) } then(onFulfilled,onRejected){ onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; } if(this.status===FULFILLED){ onFulfilled(this.reason) }else if(this.status===REJECTED){ onRejected(this.reason) }else if(this.status===PENDING){ // 4.2 回调函数就存起来了 this.#method.push({onFulfilled,onRejected}) } } } const _promise = new XPromise((resolve, reject) => { console.log('第一步执行了'); setTimeout(() => { resolve('成功') }, 2000); }) _promise.then(res => { console.log('resolve1进入', res); }, error => { console.log('reject1进入', error); }) _promise.then(res => { console.log('resolve2进入', res); }, error => { console.log('reject2进入', error); })
注意这里不是链式调用,这里只是同一个对象,多次调用then方法
异步任务
- promise 本身是同步的,但它实际上是基于异步编程的解决方案
console.log('我是第一个');
const _promise = new XPromise((resolve, reject) => {
resolve('我是第二个')
})
_promise.then(res => {
console.log('resolve1进入', res);
}, error => {
console.log('reject1进入', error);
})
console.log('我是最后一个');
//不出意外 这下面的执行顺序就是从上到下
// 我是第一个
// resolve1进入 我是第二个
// 我是最后一个
- 很明显,这样的输出结果不是我们想要的,我们希望then里面的在最后,那么我们这里就采用这三个核心API来实现(因为有浏览器兼容性问题,所以采用这三个API):
- queueMicrotask、MutationObserver、setTimeout(兜底)
- 为了能达到异步任务,我们写一个公共的方法来执行
// 为了能达到异步任务 我们写一个函数 参数是一个回调函数 function runAsyncTask(callback){ // 为了兼容 我们依次采用queueMicrotask、MutationObserver判断 if(typeof queueMicrotask==='function'){ queueMicrotask(callback) }else if(typeof MutationObserver==='function'){ // 创建MutationObserver实例化对象 const m=new MutationObserver(callback) //创建一个DOM节点 const vnode=document.createElement('p') //通过MutationObserver 观察这个DOM节点 m.observe(vnode,{childList:true}) //子节点发生改变就会触发回调函数 vnode.innerText="xxx"// 这里内容改变就会自动执行回调函数 }else{ // 兼容性都不满足 就用setTimeout 兜底 setTimeout(callback,0) } }
- 5.1 然后我们在then方法调用这个函数
执行到这里,你动手体验下就知道感受了!then(onFulfilled,onRejected){ onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; } if(this.status===FULFILLED){ //5.1 这里通过runAsyncTask改成异步任务即可 runAsyncTask(()=>{ onFulfilled(this.reason) }) }else if(this.status===REJECTED){ //5.1 这里通过runAsyncTask改成异步任务即可 runAsyncTask(()=>{ onRejected(this.reason) }) }else if(this.status===PENDING){ //5.1 PENDING 状态 我们就用runAsyncTask对onFulfilled和onRejected包装然后push到#method中 this.#method.push({ onFulfilled: () => { runAsyncTask(onFulfilled(this.reason)) }, onRejected: () => { runAsyncTask(onRejected(this.reason)) } }) } }
链式编程
- 这里应该可以理解为套娃
const _promise = new XPromise((resolve, reject) => {
resolve('我是实例化')
})
_promise.then(res=>{
console.log('这里输出实例化的结果',res)
return '给then2'
}).then(res=>{
console.log('这里应该输出第一个then的结果',res);
})
- 那么具体怎么实现呢?
- 本质上其实就是第一个then方法需要返回一个支持用.then调用的东西
- 6.1 那么我们只需要再实例化一个XPromise不就可以用then方法了
- 6.2 然后我们处理下返回值
- 6.2.1 先获取返回值
- 6.2.2 处理返回值
- 6.2.3 处理异常 通过try/catch
- 6.3 还需要考虑如果它是返回一个promise实例,那么我就需要对结果进行判断
- 6.3.1 如果是promise实例,就调用then方法返回
- 6.4 这里需要注意的一个点,就是如果在then回调函数里面,直接返回then方法的返回值 那么就会出现错误,原生Promise是会抛出错误的
- 6.4.1 所以我们也加一个 一句代码而已
_promise.then(res => { console.log('这里输出实例化的结果', res) // 情况1 return '给then2' // 情况2 throw 'throw抛出异常了' // 情况3 return new XPromise((resolve,reject)=>{ // resolve('新实例') // }) }).then(res => { console.log('这里应该输出第一个then的结果', res); }, err => { //如果抛出异常 捕获的异常结果 会在这里输出 console.log('这里异常了', err); }) //6.4 注意 这里在then回调函数里面,直接返回then方法的返回值 那么就会出现错误 const _promise1=_promise.then(res => { //_promise1 等于const定义的 _promise1 return _promise1 }, error => { console.log('reject1进入', error); }) //这里按道理是应该会抛出错误的 Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise> _promise1.then(res=>{ console.log('_promise1的成功结果',res); },err=>{ console.log('_promise1的失败结果',err); })
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; } // 6.1 这里实例化XPromise 就可以继续使用.then方法了 const newXPromise=new XPromise((resolve,reject)=>{ if (this.status === FULFILLED) { runAsyncTask(() => { try { // 6.2.1 将返回值先获取到 const returnedValue= onFulfilled(this.reason) //6.2.2 将返回值传给下一个then // resolve(returnedValue) // 6.4.1 就在这里判断一下 如果是相等抛出一个错误,而且输出是_promise1的失败结果,Chaining cycle detected for promise #<Promise> if (returnedValue === newXPromise) { throw new TypeError('Chaining cycle detected for promise #<Promise>') } // 针对情况3 我们需要做一下处理 // 6.3 判断是否是XPromise对象 if(returnedValue instanceof XPromise){ // 6.3.1 如果是XPromise对象就进一步调用then,然后返回 returnedValue.then(res=>resolve(res),err=>reject(err)) }else{ //如果不是XPromise对象将返回值直接传给下一个then resolve(returnedValue) } } catch (error) { //6.2.3 这里直接捕获异常 传给newXPromise reject(error) } }) } else if (this.status === REJECTED) { // 失败状态 执行失败函数 并且传入执行原因 runAsyncTask(() => { onRejected(this.reason) }) } else if (this.status === PENDING) { // 4.2 回调函数就存起来了 this.#method.push({ onFulfilled: () => { runAsyncTask(()=>onFulfilled(this.reason)) }, onRejected: () => { runAsyncTask(()=>onRejected(this.reason)) } }) } }) return newXPromise }
- 6.5 这里我们处理了FULFILLED 状态的返回值,那么其实REJECTED也是一样的,所以我们将一样的提取出来,写一个公共的方法
function resolvePromie(returnedValue, newXPromise, resolve, reject) {
if (returnedValue === newXPromise) {
throw new TypeError('传入的值错误,Chaining cycle detected for promise #<Promise>')
}
if (returnedValue instanceof XPromise) {
returnedValue.then(res => resolve(res), err => reject(err))
} else {
//将返回值传给下一个then
resolve(returnedValue)
}
}
//then 模块进一步改写一下
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; }
// 6.1 这里实例化XPromise 就可以继续使用.then方法了
const newXPromise=new XPromise((resolve,reject)=>{
if (this.status === FULFILLED) {
runAsyncTask(() => {
try {
// 将返回值先获取到
const returnedValue = onFulfilled(this.reason)
//6.5 调用封装的方法处理
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === REJECTED) {
// 失败状态 执行失败函数 并且传入执行原因
runAsyncTask(() => {
try {
const returnedValue = onRejected(this.reason)
//6.5 调用封装的方法处理
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === PENDING) {
this.#method.push({
onFulfilled: () => {
runAsyncTask(() =>{
try {
//6.5 调用封装的方法处理
const returnedValue = onFulfilled(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
}, onRejected: () => {
runAsyncTask(() => {
try {
//6.5 调用封装的方法处理
const returnedValue = onRejected(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
}
})
return newXPromise
}
//用这里的代码测试下 查看输出结果
const _promise = new XPromise((resolve, reject) => {
resolve('我是实例化')
})
const _promise1 = _promise.then(res => {
// return 1
// throw 'error'
// return _promise1
return new XPromise((resolve,reject)=>{
resolve('再一次')
})
}, error => {
console.log('reject1进入', error);
})
//成功失败 都会在这里输出的
_promise1.then(res => {
console.log('_promise1的成功结果', res);
}, err => {
console.log('_promise1的失败结果', err);
})
到这里,核心功能就已经完成了,每一步我都标记的有序号,根据序号去看注释部分,自己尝试下吧
方法
实例方法
之前我们已经实现了then方法,那么还有catch和finally
catch
- 7.1 其实就是添加和then一样的一个实例方法,参考下MDN就明白了
- 7.2 但是如果实例化的时候出现异常的话,我们就需要处理下异常
- 实例化的时候 捕获异常在 1.4 执行回调函数 的时候处理
//7.1 在then下面添加catch实例方法,内部调用then方法即可
catch(onRejected){
return this.then(undefined,onRejected)
}
//7.2 然后改一下构造函数里面的返回,加一个try/catch包裹,返回错误即可
// 1.4 执行回调函数
try {
callback(resolve, reject)
} catch (error) {
reject(error)
}
finally
看MDN的描述就很简单,就是内部调用then方法,传入两个一样的参数即可
- 8.1 在catch下面添加finally实例方法
//8.1 添加finally实例方法
finally(onFinally){
return this.then(onFinally,onFinally)
}
上面我们实现了实例方法,下面就实现静态方法
静态方法
graph LR
resolve --> reject-->race-->all-->allSettled-->any
XPromise.resolve(值)
- 对传入的值进行判断(可参考MDN)
- 9.1 如果是普通值,转为XPromise并返回
- 9.2 如果是XPromise,直接返回
- 9.3 静态方法测试代码
- 9.4 reject方法
- 9.4.1
Promise.reject()
静态方法返回一个已拒绝(rejected)的Promise
对象,拒绝原因为给定的参数(MDN参考)。 - 9.4.2 reject静态方法测试代码
- 9.4.1
- 9.1 如果是普通值,转为XPromise并返回
// resolve静态方法
static resolve(value){
// 9.2 判断是否是XPromise
if(value instanceof XPromise){
return value
}
// 9.1 如果是普通值
return new XPromise((resolve)=>resolve(value))
}
// 9.4.1 reject静态方法
static reject(value){
return new XPromise((resolve,reject)=>reject(value))
}
// 9.3 静态方法测试
XPromise.resolve(new XPromise((resolve,reject)=>{
// resolve('静态方法初始化')
// reject('静态方法初始化')
// throw '静态方法初始化'
})).then(res=>{
console.log('静态方法成功',res);
},error=>{
console.log('静态方法失败',error);
})
XPromise.resolve('普通值').then(res=>{
console.log(res);
})
// 9.4.2 静态方法测试代码
XPromise.reject('reject普通值').catch(res=>{
console.log(res);
})
- race方法
- 其实就是谁最快返回结果
- 测试代码
//静态 race方法
static race(value){
// 返回XPromise
return new XPromise((resolve,reject)=>{
// 本来接收的是一个数组,但是不排除非数组;判断是否是数组不是数组抛出错误
if(!Array.isArray(value)){
return reject(new TypeError('不是一个数组哦~'))
}
// 这里传递的肯定是一个数组 所以我们遍历数组
value.forEach(item=>{
// 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
XPromise.resolve(item).then(res=>{
resolve(res)
},err=>{
reject(err)
})
})
})
}
//race测试代码
const _promise1=new XPromise((resolve,reject)=>{
setTimeout(() => {
resolve('race1实例')
}, 1000);
})
const _promise2=new XPromise((resolve,reject)=>{
setTimeout(() => {
reject('race2实例')
}, 500);
})
//如果去掉普通值,那么结果肯定是输出:(race结果失败,race2实例)
XPromise.race([_promise1,_promise2,'普通值']).then(res=>{
console.log('race结果正常',res);
},err=>{
console.log('race结果失败',err);
})
- all方法
- 要么都成功,要么返回第一个失败的
- 首先要判断是否是数组
- 空数组直接返回
- 数组的话 进一步处理
static all(value) {
// 返回XPromise实例
return new XPromise((resolve, reject) => {
// 判断是否是数组 不是数组 抛出错误
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
//如果是空数组,直接返回
value.length === 0 && resolve(value)
// 为了让我们执行的顺序跟传入的顺序保持一致
// 定义一个数组,将结果按顺序存起来
let results = []//存储结果
let count = 0 //记录返回的次数
value.forEach((item,index) => {
// 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
XPromise.resolve(item).then(res => {
results[index] = res
count++
// 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
count === value.length && resolve(results)
// 保证结果的顺序跟数组的顺序一致
}, err => {
// 只要第一个拒绝就返回
reject(err)
})
})
})
}
//all 代码测试
const _promise1 = new XPromise((resolve, reject) => {
setTimeout(() => {
resolve('all1实例')
}, 1000);
})
const _promise2 = new XPromise((resolve, reject) => {
setTimeout(() => {
resolve('all2实例')
reject('all2实例')//如果这里执行,那么结果一定是失败结果
}, 500);
})
const _promise3 = 3
XPromise.all([_promise1, _promise2,_promise3]).then(res => {
console.log('all结果正常', res);
}, err => {
console.log('all结果失败', err);
})
- allSettled
- 传的是数组,得到的也是一个对象数组
- [{ status: 'fulfilled', value: 33 },{ status: 'rejected', reason: Error: 一个错误 }]
- 结果顺序跟传入的数组顺序一致
- 不是数组会报错
- 跟all方法基本一致,就是返回结果不一样
- 不管成功还是失败,都在then的第一个回调函数
- 传的是数组,得到的也是一个对象数组
//allSettled 静态方法
static allSettled(value){
// 返回XPromise实例
return new XPromise((resolve, reject) => {
// 判断是否是数组 不是数组 抛出错误
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
//如果是空数组,直接返回
value.length === 0 && resolve(value)
let results = []//存储结果
let count = 0 //记录返回的次数
value.forEach((item,index) => {
// 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
XPromise.resolve(item).then(res => {
results[index] = {status:FULFILLED,value:res}
count++
// 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
count === value.length && resolve(results)
// 保证结果的顺序跟数组的顺序一致
}, err => {
results[index] = {status:REJECTED,reason:err}
count++
// 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
count === value.length && resolve(results) //无论成功还是失败 都是已完成 用resolve
})
})
})
}
//allSettled 测试代码
const _promise1 = new XPromise((resolve, reject) => {
setTimeout(() => {
resolve('allSettled1实例')
}, 1000);
})
const _promise2 = new XPromise((resolve, reject) => {
setTimeout(() => {
reject('allSettled2实例')
}, 500);
})
const _promise3 = 3
XPromise.allSettled([_promise1, _promise2,_promise3]).then(res => {
console.log('allSettled结果正常', res);//不管成功还是失败 都会进入这里
}, err => {
console.log('allSettled结果失败', err);//这里是不会进入的
})
- any
- 都失败的话,会返回一个错误的数组,数组里面记录了每一个错误的原因
- 空数组的话,也是一个错误,但是错误里面的数组是空的
//静态 any方法
static any(value){
// 返回XPromise实例
return new XPromise((resolve, reject) => {
// 判断是否是数组 不是数组 抛出错误
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
//如果是空数组,直接返回
// value.length === 0 && reject('不能是空数组,小可爱~~') new AggregateError Promise是这样抛的
value.length === 0 && reject(new AggregateError(value,'不能是空数组呀'))
let errors = []//存储错误结果; 正常的直接返回了
let count = 0 //记录返回的次数
value.forEach((item,index) => {
// 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
XPromise.resolve(item).then(res => {
resolve(res)
}, err => {
errors[index] =err
count++
// 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
count === value.length && reject(new AggregateError(errors,'完蛋了~,都是reject'))
})
})
})
}
// any 测试代码
const _promise1 = new XPromise((resolve, reject) => {
setTimeout(() => {
reject('any1实例')
}, 1000);
})
const _promise2 = new XPromise((resolve, reject) => {
setTimeout(() => {
reject('anyd2实例')
}, 500);
})
const _promise3 = XPromise.reject(3)
XPromise.any([_promise1, _promise2,_promise3]).then(res => {
console.log('any结果正常', res);
}, err => {
console.log('any结果失败', err);
//如果都是reject,会返回一个数组
console.dir(err);//dir能看到详细报错数组信息
})
到这里,基本都完成了,最后一个promise/A+规范测试了
单元测试
- 使用社区提供的包promises-aplus-tests测试
- 具体规范参考:promisesaplus.com/
完整代码
- 删掉了部分注释,方便看的更清楚些
- 这里的代码其实缺少符合A+规范,但是手写Promise大概流程就这样
- 自己学习使用,代码没有做优化,很多地方可以优化(主要是懒)
const PENDING = "pending", FULFILLED = "fulfilled", REJECTED = "rejected";// 2.1 定义全局状态
function runAsyncTask(callback) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(callback)
} else if (typeof MutationObserver === 'function') {
const m = new MutationObserver(callback)
const vnode = document.createElement('p')
m.observe(vnode, { childList: true })
vnode.innerText = "change"// 这里内容改变就会自动执行回调函数
} else {
setTimeout(callback, 0)
}
}
function resolvePromie(returnedValue, newXPromise, resolve, reject) {
if (returnedValue === newXPromise) {
throw new TypeError('传入的值错误,Chaining cycle detected for promise #<Promise>')
}
if (returnedValue instanceof XPromise) {
returnedValue.then(res => resolve(res), err => reject(err))
} else {
resolve(returnedValue)
}
}
class XPromise {
status = PENDING // 2.2 用status实例属性设置状态,默认状态是pending
reason = undefined //2.2 用reason实例属性来设置原因,初始原因是undefined
#method = [] //[{onFulfilled,onRejected}...]
constructor(callback) {
const resolve = (res) => {
if (this.status === PENDING) {
this.status = FULFILLED // 2.4 成功是fulfilled 状态
this.reason = res // 2.4 记录成功原因
this.#method.forEach(({ onFulfilled }) => onFulfilled(this.reason))
}
}
const reject = (error) => {
if (this.status === PENDING) {
this.status = REJECTED // 2.4 失败是rejected 状态
this.reason = error // 2.4 记录失败原因
this.#method.forEach(({ onRejected }) => onRejected(this.reason))
}
}
try {
callback(resolve, reject)
} catch (error) {
console.log('error', error);
reject(error)
}
}
//then实例方法
then(onFulfilled, onRejected) {
// 3.2 这里首先得判断传入的是不是一个函数 如果是函数就执行 不是函数 就参考的MDN文档直接返回
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; }
// 这里实例化XPromise 就可以继续使用.then方法了
const newXPromise = new XPromise((resolve, reject) => {
//3.3 根据不同的状态执行不同的方法,返回不同的值
if (this.status === FULFILLED) {
// 成功状态 执行成功函数 并且传入执行原因
runAsyncTask(() => {
try {
// 将返回值先获取到
const returnedValue = onFulfilled(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === REJECTED) {
// 失败状态 执行失败函数 并且传入执行原因
runAsyncTask(() => {
try {
const returnedValue = onRejected(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === PENDING) {
// 4.2 回调函数就存起来了
this.#method.push({
onFulfilled: () => {
runAsyncTask(() => {
try {
const returnedValue = onFulfilled(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
}, onRejected: () => {
runAsyncTask(() => {
try {
const returnedValue = onRejected(this.reason)
resolvePromie(returnedValue, newXPromise, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
}
})
return newXPromise
}
//添加catch实例方法
catch(onRejected) {
this.then(undefined, onRejected)
}
//添加finally实例方法
finally(onFinally) {
return this.then(onFinally, onFinally)
}
/**-------静态方法--------- */
static resolve(value) {
if (value instanceof XPromise) {
return value
}
return new XPromise((resolve) => resolve(value))
}
static reject(value) {
return new XPromise((resolve, reject) => reject(value))
}
static race(value) {
return new XPromise((resolve, reject) => {
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
value.forEach(item => {
XPromise.resolve(item).then(res => {
resolve(res)
}, err => {
reject(err)
})
})
})
}
static all(value) {
return new XPromise((resolve, reject) => {
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
value.length === 0 && resolve(value)
let results = []//存储结果
let count = 0 //记录返回的次数
value.forEach((item,index) => {
XPromise.resolve(item).then(res => {
results[index] = res
count++
count === value.length && resolve(results)
}, err => {
reject(err)
})
})
})
}
static allSettled(value){
return new XPromise((resolve, reject) => {
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
value.length === 0 && resolve(value)
let results = []//存储结果
let count = 0 //记录返回的次数
value.forEach((item,index) => {
XPromise.resolve(item).then(res => {
results[index] = {status:FULFILLED,value:res}
count++
count === value.length && resolve(results)
}, err => {
results[index] = {status:REJECTED,reason:err}
count++
count === value.length && resolve(results) //无论成功还是失败 都是已完成 用resolve
})
})
})
}
static any(value){
return new XPromise((resolve, reject) => {
if (!Array.isArray(value)) {
return reject(new TypeError('不是一个数组哦~'))
}
value.length === 0 && reject(new AggregateError(value,'不能是空数组呀'))
let errors = []//存储错误结果 正常的 直接返回了
let count = 0 //记录返回的次数
value.forEach((item,index) => {
XPromise.resolve(item).then(res => {
resolve(res)
}, err => {
errors[index] =err
count++
count === value.length && reject(new AggregateError(errors,'不能是空数组呀')) //无论成功还是失败 都是已完成 用resolve
})
})
})
}
}
// 静态方法测试
XPromise.resolve(new XPromise((resolve, reject) => {
// resolve('静态方法初始化')
// reject('静态方法初始化')
// throw '静态方法初始化'
})).then(res => {
console.log('静态方法成功', res);
}, error => {
console.log('静态方法失败', error);
})
const _promise1 = new XPromise((resolve, reject) => {
setTimeout(() => {
reject('any1实例')
}, 1000);
})
const _promise2 = new XPromise((resolve, reject) => {
setTimeout(() => {
reject('anyd2实例')
}, 500);
})
const _promise3 = XPromise.reject(3)
XPromise.any([_promise1, _promise2,_promise3]).then(res => {
console.log('any结果正常', res);
}, err => {
console.log('any结果失败', err);
console.dir(err);//dir能看到详细报错数组信息
})
// const _promise1 = new XPromise((resolve, reject) => {
// setTimeout(() => {
// resolve('allSettled1实例')
// }, 1000);
// })
// const _promise2 = new XPromise((resolve, reject) => {
// setTimeout(() => {
// reject('allSettled2实例')
// }, 500);
// })
// const _promise3 = 3
// XPromise.allSettled([_promise1, _promise2,_promise3]).then(res => {
// console.log('allSettled结果正常', res);
// }, err => {
// console.log('allSettled结果失败', err);
// })
// const _promise1 = new XPromise((resolve, reject) => {
// setTimeout(() => {
// resolve('all1实例')
// }, 1000);
// })
// const _promise2 = new XPromise((resolve, reject) => {
// setTimeout(() => {
// resolve('all2实例')
// }, 500);
// })
// const _promise3 = 3
// XPromise.all([_promise1, _promise2,_promise3]).then(res => {
// console.log('all结果正常', res);
// }, err => {
// console.log('all结果失败', err);
// })
// XPromise.race([_promise1,_promise2]).then(res=>{
// console.log('race结果正常',res);
// },err=>{
// console.log('race结果失败',err);
// })
// XPromise.resolve('resolve普通值').then(res=>{
// console.log(res);
// })
// XPromise.reject('reject普通值').catch(res=>{
// console.log(res);
// })
// const _promise = new XPromise((resolve, reject) => {
// setTimeout(() => {
// // resolve('我是实例化')
// // reject('我是catch实例化')
// throw 'throw抛出异常了'
// }, 1000);
// })
// _promise.then(res => {
// console.log('实例方法成功', res);
// }).catch(err => {
// console.log('实例方法失败', err);
// }).finally(()=>{
// console.log('finally');
// })
// _promise.then(res => {
// console.log('resolve1进入', res);
// resolve('第一个then到第二个then执行')
// }, error => {
// console.log('reject1进入', error);
// })
// _promise.then(res => {
// console.log('这里输出实例化的结果', res)
// // return '给then2'
// // throw 'throw抛出异常了'
// return new XPromise((resolve, reject) => {
// resolve('新实例')
// })
// }).then(res => {
// console.log('这里应该输出第一个then的结果', res);
// }, err => {
// console.log('这里异常了', err);
// })
// const _promise1 = _promise.then(res => {
// // return 1
// // throw 'error'
// // return _promise1
// return new XPromise((resolve, reject) => {
// resolve('再一次')
// })
// }, error => {
// console.log('reject1进入', error);
// })
// _promise1.then(res => {
// console.log('_promise1的成功结果', res);
// }, err => {
// console.log('_promise1的失败结果', err);
// })
// console.log('我是最后一个11');
// _promise.then(res => {
// console.log('resolve2进入', res);
// }, error => {
// console.log('reject2进入', error);
// })
// _promise.then(res=>{
// console.log('成功',res);
// }).catch(err=>{
// console.log('catch',err);
// }).finally(()=>{
// console.log('finally');
// })