Promise是实现异步表现的一种方式,存在pending,fulfilled,rejected三种状态,微任务的一种
1.基本结构
- 初始化状态
- 创建reslove和reject方法
- 创建基本的then方法
- 实例化的时候立即执行reslove/reject相关的操作
const PENDING = "PENDING" //待定状态,也就是还有完成的状态
const FULFILLED = "FULFILLED" //操作成功状态
const REJECTED = "REJECTED" //操作失败状态
class MyPromise(){
constructor(excutor){
//promise会传入一个立即执行的函数,传入的函数其中包含resolve和reject连个函数参数
//把内部的reslove和reject分别传入代替的参数函数,其实就是一个回调
//用箭头函数,是因为在外部调用的,为了内部this成功绑定实例,否则可能this绑定失败
try{
excutor(this.reslove, this.reject)
}catch(err){
this.reject(err)
}
}
status = PENDING //当前的状态
value = null //成功的值
reason = null //失败的值
reslove = (value) => {
if(this.status === PENDING){ //执行完成函数的时候,如果状态是等待则设置为成功
this.status === FULFILLED
this.value = value //设置成功的值
}
} //成功的执行函数
reject = (reason) => {
if(this.status === PENDING){ //执行失败函数的时候,如果状态是等待则设置为失败
this.status === REJECTED
this.reason = reason //设置失败的值
}
} //失败的执行函数
then(onResolve, onReject){
if(this.status === FULFILLED){
onResolve(this.value)
} else if (this.status === REJECTED){
onReject(this.reason)
} else if (this.status === PENDING){
...
}
}
}
2.实现异步操作
- 基本结构只能实现同步,如果异步则需要对成功和失败分别创建一个队列,然后等待时机再执行
- 在then函数内部把对应的操作推入对应的队列
class MyPromise(){
...
onFulfilledQueue = [] //成功操作的队列
onRejectedQueue = [] //失败操作的队列
reslove = (value) => {
if(this.status === PENDING){ //执行完成函数的时候,如果状态是等待则设置为成功
...
while(this.onFulfilledQueue.length > 0){
//如果队列还有操作函数,则依次推出执行,知道队列为空
const itemFn = this.onFulfilledQueue.shift()
if(typeof itemFn === "function") itemFn(this.value)
}
}
} //成功的执行函数
reject = (reason) => {
if(this.status === PENDING){ //执行失败函数的时候,如果状态是等待则设置为失败
...
while(this.onRejectedQueue.length > 0){
//如果队列还有操作函数,则依次推出执行,知道队列为空
const itemFn = this.onRejectedQueue.shift()
if(typeof itemFn === "function") itemFn(this.value)
}
}
} //失败的执行函数
then(onResolve, onReject){
if(this.status === FULFILLED){
onResolve(this.value)
} else if (this.status === REJECTED){
onReject(this.reason)
} else if (this.status === PENDING){
this.onFulfilledQueue.push(onResolve)
this.onRejectedQueue.push(onReject)
}
}
}
3.实现链式调用
- 由于要实现链式调用所以then函数内部需要返回一个同样的promise
- 需要等到实例完成才能在内部使用当前实例,所以需要在当前同步的添加一个微任务操作
- 简单的可以用setTimout,但是这样相当于开了个宏任务,为了更好操作推荐queueMicrotask来开一个微任务
- 错误处理,比如then处理的值不能是当前的promise否则就会死循环
function reslovePromise = (p, x, resolve, reject){
//创建一个helper方便操作每一步的then的resolve值
//then参数依旧是一个回调,用于处理每一步的值
//x为当前then计算后的值
//p为当前的promise实例
if(p === x){
//如果计算出来的值就是当前的promise则会报循环错误
retrun reject(new Error())
}
if(x instanceof MyPromise){
//如果值是一个promise则直接调用then
x.then(resolve, reject)
}else{
//否则直接执行完成函数
resolve(x)
}
}
class MyPromise(){
...
then(onResolve, onReject){
//由于then参数可以不传所以需要兼容,成功则返回值,失败抛出错误
onResolve = typeof onResolve === "function" ? onResolve : value => value
onReject = typeof onReject === "function" ? onReject : reason => throw reason
const p = new MyPromise((resolve, reject) => {
//创建一个新的实例,然后返回改实例实现链式调用
cosnt onFulfilledTaskHandle = () => { //完成时候的操作
queueMicrotask(() => {
//由于要用到p,所以添加一个微任务即可获取到
//否则就会取不到报错,因为p还没有创建好
try{
//因为计算可以是任何类型,也可能报错,所以需要错误处理
const x = onResolve(this.value) //计算新的值
reslovePromise(p, x, resolve, reject)
}catch(err){
reject(err)
}
})
}
cosnt onRejectedTaskHandle = () => { //错误时候的操作
queueMicrotask(() => {
//由于要用到p,所以添加一个微任务即可获取到
//否则就会取不到报错,因为p还没有创建好
try{
//因为计算可以是任何类型,也可能报错,所以需要错误处理
const x = onReject(this.reson) //计算新的错误值
reslovePromise(p, x, resolve, reject)
}catch(err){
reject(err)
}
})
}
if(this.status === FULFILLED){
onFulfilledTaskHandle()
} else if (this.status === REJECTED){
onRejectedTaskHandle()
} else if (this.status === PENDING){
this.onFulfilledQueue.push(onFulfilledTaskHandle)
this.onRejectedQueue.push(onRejectedTaskHandle)
}
})
return p
}
}
4.添加resolve/reject静态方法
- static或直接MyPromise.[key]
class MyPromise(){
...
static resolve(value){
if(value instanceof MyPromise) return value
return new MyPromise(resolve => {resolve(value)}) //直接成功于传入的值
}
static reject(reason){
return new MyPromise((resolve, reject) => {reject(reason)}) //直接失败原因
}
}
5.添加catch和finally
- catch利用then直接reject
- finally利用then直接返回对应的,因为不管成功还是失败都会执行
class MyPromise(){
...
catch(onRejected){
return this.then(null, onRejected) //不传入成功参数
}
finally(callback){
return this.then(
data => { //如果成功则走此,直接返回一个成功的
return MyPromise.resolve(callback()).then(() => data)
},
error => { //如果失败则走此,直接返回一个失败的抛出一个错误error
return MyPromise.reject(callback()).then(() => {throw error})
}
)
}
}
6.添加all,race,allSattled静态方法
- all-返回全部成功的,如果有一个失败的都不返回
- allSattled-返回所有的结果包括成功和失败
- race最快的返回
class MyPromise(){
...
static all(promiseArr){
return new MyPromise((resolve, reject) => {
let res = []
let counts = promiseArr.length
promiseArr.forEatch((p, idx) => {
p.then(data => {
res[idx] = data
counts -= 1
if(counts === 0) resolve(res)
}, reject())
})
})
}
static allSattled(promiseArr){
return new MyPromise((resolve, reject) => {
let res = []
let counts = promiseArr.length
promiseArr.forEatch((p, idx) => {
p.then(
data => {
res[idx] = { status: true, data }
counts -= 1
if(counts === 0) resolve(res)
},
err => {
res[idx] = { status: false, err }
counts -= 1
if(counts === 0) reject(res)
}
)
})
})
}
static race(promiseArr){
return new MyPromise((resolve, reject) => {
for(let p of promiseArr){
p.then(resolve, reject)
}
})
}
}
7.最终实现
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
function resolvePromise(p, x, res, rej){
if(p === x){
return rej(new TypeError("promise cycle error"))
}
if(x instanceof MyPromise){
x.then(res, rej)
}else {
res(x)
}
}
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
}catch (error){
this.reject(error)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallBackQueue = []
onRejectedCallBackQueue = []
resolve = (value) => {
if(this.status === PENDING){
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallBackQueue.length){
const itemFun = this.onFulfilledCallBackQueue.shift()
if(typeof itemFun === "function"){
itemFun(value)
}
}
}
}
reject = (reason) => {
if(this.status === PENDING){
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallBackQueue.length){
const itemFun = this.onRejectedCallBackQueue.shift()
if(typeof itemFun === "function"){
itemFun(reason)
}
}
}
}
then(onFulfilled, onRejected){
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
onRejected = typeof onRejected === "function" ? onRejected : reason => {throw reason}
const p = new MyPromise((resolve, reject) => {
const fulfilledTask = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p, x, resolve, reject)
}catch (error){
reject(error)
}
})
}
const rejectedTask = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p, x, resolve, reject)
}catch (error){
reject(error)
}
})
}
if(this.status === FULFILLED){
fulfilledTask()
} else if(this.status === REJECTED){
rejectedTask()
} else if(this.status === PENDING){
this.onFulfilledCallBackQueue.push(fulfilledTask)
this.onRejectedCallBackQueue.push(rejectedTask)
}
})
return p
}
catch(onRejected){
return this.then(null, onRejected)
}
finally(callback){
return this.then(data => {
return MyPromise.resolve(callback()).then(() => data)
}, error => {
return MyPromise.reject(callback()).then(() => {throw error})
})
}
static resolve(value){
if(value instanceof MyPromise){
return value
}
return new MyPromise(resolve => {
resolve(value)
})
}
static reject(reason){
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
static all(promiseArr){
return new MyPromise(function (resolve, reject){
let res = []
let counts = promiseArr.length
promiseArr.forEach((p, idx) => {
p.then(data => {
res[idx] = data
counts -= 0
if(counts === 0) resolve(res)
}, reject())
})
})
}
static allSettled(promiseArr){
return new MyPromise(function (resolve, reject){
let res = []
let counts = promiseArr.length
promiseArr.forEach((p, idx) => {
p.then(value => {
res[idx] = {
status: FULFILLED,
value
}
counts -= 1
if(counts === 0) resolve(res)
}, reason => {
res[idx] = {
status: REJECTED,
reason
}
counts -= 1
if(counts === 0) resolve(res)
})
})
})
}
static race(promiseArr){
return new MyPromise(function (resolve, reject){
for(let p of promiseArr){
p.then(resolve, reject)
}
})
}
}
```