先看一下PromiseA+规范:
- 一个Promise必须处在其中之一的状态:
pending, fulfilled
或rejected
,pending
可以向后两种状态转移,而后两种状态是稳定的,进入之后就永远不会变更且附带一个状态值 - 提供then方法来以访问其当前的值、终值和拒因
- then方法接受两个参数,onFulfilled是当Promise状态变为Fullfilled的状态的回调,onRejected是当Promise状态变为Rejected的回调,两个参数都必须是函数,并且满足1.promise之前结束前不可调用 。2.调用次数不超过一次。3.值不是函数忽略。
- then返回一个promise方法,下一次then受上一次then返回结果的影响
总结一下我们要实现的:
-
维护promise实例内部状态,PENDING,FULLFILLED,REJECTED,实现resolve,reject方法来进行状态的单向流转
-
实现一个then方法:
-
接受两个函数作为异步回调
-
then方法可以进行链式的调用,以上一次的返回值来决定下一次Promise的状态
-
对于接收函数的返回值要进行类型判断的处理
-
-
一些优化
-
对于resolve,reject接收的值夜要进行类型的判断进行不同的处理
-
all,race等一些静态方法的实现
-
下面我们开始一步一步的实现我们的Promise
一、维护promise实例内部状态,PENDING,FULLFILLED,REJECTED,实现resolve,reject方法来进行状态的单向流转
// 定义3个状态常量,如果用TS的话可以用枚举
const PENDING = 'PENDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'
// 要实现的promise类
class MyPromise {
status
value
reason
constructor(exec) {
// 初始状态为PENDING
this.status = PENDING
exec(this.resolve.bind(this), this.reject.bind(this))
}
resolve(value) {
// 状态仅可以变更一次
if (this.status === PENDING) {
this.value = value
this.status = FULLFILLED
}
}
reject(reason) {
// 状态仅可以变更一次
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
}
二、then方法的实现
这里分几步来依次完善then方法
-
实现异步回调和链式调用
链式调用只要返回一个新的promise即可
then(onFullfilled, onRejected) {
// 返回 promise
let newP = new MyPromise((resolve, reject) => {
// 将传入的两个函数封装成微任务
const fullfilled = val => {
queueMicrotask(() => {
let res = onFullfilled(val)
resolve(res)
})
}
const rejceted = reason => {
queueMicrotask(() => {
let res = onRejected(reason)
reject(res)
})
}
// 如果状态已经是FULLFILLED,立刻执行fullfilled方法
if (this.status === FULLFILLED) {
fullfilled(this.value)
}
// 如果状态已经是REJECTED,立刻执行rejceted方法
if (this.status === REJECTED) {
rejceted(this.reason)
}
// 如果状态是PENDING,将两个任务推入待执行队列
if (this.status === PENDING) {
this.successFnList.push(fullfilled)
this.errorFnList.push(rejceted)
}
})
return newP
}
-
对传入函数的执行的返回值进行处理
我们知道还有这样一个规则,传入的两个回调函数的返回值如果是一个promise,那么.then方法所创建的新的promise的状态将会有这个return的promise来决定,所以我们需要一个函数来处理不同类型的返回值
先写出这个函数
/**
* @param {*} promise 执行回调函数的那个promise
* @param {*} x 回调函数执行返回值
* @param {*} resolve promise中的resolve
* @param {*} reject promise中的reject
* @param {*} end 这个值在fullfilled中是resolve,在rejected中是reject,在如果x的值是普通类型,将会由end来处理
*/
function handlePromiseEnd(promise, x, resolve, reject, end) {
// 避免套娃
if (promise === x) {
return reject(new Error('error'))
}
if (typeof x === 'function') {
// 如果是方法,执行完,对结果再判断一次
let rx = x()
handlePromiseEnd(promise, rx, resolve, reject, end)
} else if (x instanceof MyPromise) {
// 如果是promise,end走那条路就交给promise来决定了
let onResolved = val => {
handlePromiseEnd(x, val, resolve, reject, resolve)
}
let onRejected = reason => {
handlePromiseEnd(x, reason, resolve, reject, reject)
}
// 别问我为什么非要套个微任务,源码就是这样写的,我能怎么办呢
queueMicrotask(() => {
x.then(onResolved, onRejected)
})
} else {
// 直到结果是一个普通值,调用end处理
end(x)
}
}
最终的then函数就是这样
then(onFullfilled, onRejected) {
// 返回 promise
let newP = new MyPromise((resolve, reject) => {
// 将传入的两个函数封装成微任务
const fullfilled = val => {
queueMicrotask(() => {
let res = onFullfilled(val)
handlePromiseEnd(newP, res, resolve, reject, resolve)
})
}
const rejceted = reason => {
queueMicrotask(() => {
let res = onRejected(reason)
handlePromiseEnd(newP, res, resolve, reject, reject)
})
}
// 如果状态已经是FULLFILLED,立刻执行fullfilled方法
if (this.status === FULLFILLED) {
fullfilled(this.value)
}
// 如果状态已经是REJECTED,立刻执行rejceted方法
if (this.status === REJECTED) {
rejceted(this.reason)
}
// 如果状态是PENDING,将两个任务推入待执行队列
if (this.status === PENDING) {
this.successFnList.push(fullfilled)
this.errorFnList.push(rejceted)
}
})
return newP
}
三、进一步完善
promise主要部分已经完成,还需要完善两个方面
1. 对resolve,rejcet接收的值进行不同类型的处理
2. 添加all,race,resolve,reject4个静态方法
这里直接贴上全部代码
// 1. 状态的单向流转
// 2. then 异步回调,链式调用,返回值处理
// 3. resolve,reject在接收promise时的状态移交
// 4. 静态方法:resolve,reject,race,all
const PENDING = 'PENDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
status
value
reason
successFnList
errorFnList
constructor(exec) {
this.status = PENDING
this.successFnList = []
this.errorFnList = []
exec(this.resolve.bind(this), this.reject.bind(this))
}
resolve(value) {
if (this.status === PENDING) {
if (value instanceof MyPromise) {
value.then(this.resolve.bind(this), this.reject.bind(this))
} else {
this.value = value
this.status = FULLFILLED
this.successFnList.forEach(fn => {
fn(this.value)
})
}
}
}
reject(reason) {
if (this.status === PENDING) {
if (reason instanceof MyPromise) {
value.then(this.resolve.bind(this), this.reject.bind(this))
} else {
this.reason = reason
this.status = REJECTED
this.errorFnList.forEach(fn => {
fn(this.reason)
})
}
}
}
then(onFullfilled, onRejected) {
// 返回 promise
let newP = new MyPromise((resolve, reject) => {
// 将传入的两个函数封装成微任务
const fullfilled = val => {
queueMicrotask(() => {
let res = onFullfilled(val)
handlePromiseEnd(newP, res, resolve, reject, resolve)
})
}
const rejceted = reason => {
queueMicrotask(() => {
let res = onRejected(reason)
handlePromiseEnd(newP, res, resolve, reject, reject)
})
}
// 如果状态已经是FULLFILLED,立刻执行fullfilled方法
if (this.status === FULLFILLED) {
fullfilled(this.value)
}
// 如果状态已经是REJECTED,立刻执行rejceted方法
if (this.status === REJECTED) {
rejceted(this.reason)
}
// 如果状态是PENDING,将两个任务推入待执行队列
if (this.status === PENDING) {
this.successFnList.push(fullfilled)
this.errorFnList.push(rejceted)
}
})
return newP
}
static resolve(value) {
if (value instanceof MyPromise) {
return value
}
return new MyPromise((resolve, rejcet) => {
resolve(value)
})
}
static reject(reason) {
if (reason instanceof MyPromise) {
return reason
}
return new MyPromise((resolve, rejcet) => {
rejcet(reason)
})
}
static race(promiseList) {
if (promiseList.length === 0) {
return
}
return new MyPromise((resolve, rejcet) => {
promiseList.forEach(promise => {
promise.then(
res => {
resolve(res)
},
reason => {
reject(reason)
}
)
})
})
}
static all(promiseList) {
if (promiseList.length === 0) {
return
}
return new MyPromise((resolve, rejcet) => {
let count = 0
const resultList = []
for (let index = 0; index < promiseList.length; index++) {
const promise = promiseList[index]
promise.then(
res => {
count++
resultList[index] = res
if (count === promiseList.length) {
resolve(resultList)
}
},
reason => {
reject(reason)
}
)
}
})
}
}
/**
* @param {*} promise 执行回调函数的那个promise
* @param {*} x 回调函数执行返回值
* @param {*} resolve promise中的resolve
* @param {*} reject promise中的reject
* @param {*} end 这个值在fullfilled中是resolve,在rejected中是reject,在如果x的值是普通类型,将会由end来处理
*/
function handlePromiseEnd(promise, x, resolve, reject, end) {
// 避免套娃
if (promise === x) {
return reject(new Error('error'))
}
if (typeof x === 'function') {
// 如果是方法,执行完,对结果再判断一次
let rx = x()
handlePromiseEnd(promise, rx, resolve, reject, end)
} else if (x instanceof MyPromise) {
// 如果是promise,end走那条路就交给promise来决定了
let onResolved = val => {
handlePromiseEnd(x, val, resolve, reject, resolve)
}
let onRejected = reason => {
handlePromiseEnd(x, reason, resolve, reject, reject)
}
// 别问我为什么非要套个微任务,源码就是这样写的,我能怎么办呢
queueMicrotask(() => {
x.then(onResolved, onRejected)
})
} else {
// 直到结果是一个普通值,调用end处理
end(x)
}
}
最后,我这里并没有添加try catch捕获异常,各位可以自行添加