首先我们先去了解一下Promise/A+规范.
Promise 相关术语
- 解决(fulfill): 指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用
fulfill来表示解决,但在后来的 promise 实现多以resolve来指代之。 - 拒绝(reject):指一个 promise 失败时进行的一系列操作。
- 终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待状态的结束,故称之终值,有时也直接简称为值(value)。
- 据因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
Promise 的状态
一个 Promise 的当前状态必须为以下三种状态中的一种:等待状态(Pending)、执行状态(Fulfilled) 和 拒绝状态(Rejected) 。
等待状态(Pending)
处于等待状态时,promise 需满足以下条件:
- 可以迁移至执行状态或拒绝状态
执行状态(Fulfilled)
处于执行状态时,promise 需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的终值
拒绝状态(Rejected)
处于拒绝状态时,promise 需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的据因
这里不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变。(当 value 或 reason 不是基本值时,只要求其引用地址相等,但属性值可被更改)。
Then 方法
一个 promise 必须提供一个 then 方法以访问其当前值,终值和据因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
参数可选
onFulfilled 和 onRejected 都是可选参数。
- 如果
onFulfilled不是函数,其必须被忽略 - 如果
onRejected不是函数,其必须被忽略
onFulfilled 特征
如果 onFulfilled 是函数:
- 当
promise执行接受后其必须被条用,其第一个参数为promise的终值 - 在
promise执行结束前其不可被调用 - 其调用次数不可超过一次
onRejected 特征
如果 onRejected 是函数
- 当
promise被拒绝执行后其必须被调用,其第一个参数为promise的据因 - 在
promise被拒绝执行前其不可被调用 - 其调动次数不可超过一次
更过规范请自行查看 Promise/A+ 规范
实现一个简易版 Promise
首先先搭建构建函数的大体框架
- 首先创建三个常量用于表示状态
- 在函数体内部创建常量
that,因为代码可能会异步执行,用于获取正确的this对象 - 一开始
Promise的状态应该为pengding value变量用于保存resolve或者reject中传入的值resolvedCallbacks和rejectedCallbacks用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,这时候应该吧then中的回调保存起来用于状态改变时使用
const PENDING = 'pengding'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this
this.state = PENDING
this.value = null
this.resolvedCallbacks = []
this.rejectedCallbacks = []
// 待完善 resolve 和 reject 函数
// 待完善执行 fn 函数
}
接下来来完善resolve和rejected函数,添加在MyPromise函数内部
- 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待状态才可以改变状态
- 将当前状态更改为对应状态,并且将传入的值赋值给
value - 遍历回调数组并执行
function resolve(value){
if(that.state === PENDING){
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map((cb) => cb(that.value))
}
}
function rejected(value){
if(that.state === PENDING){
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map((cb) => cb(that.value))
}
}
完成以上两个函数以后,就应该实现如何执行Promise中传入的函数了
- 执行传入的参数并且将之前两个函数当做参数传进去
- 要注意的是,可能执行函数过程中会遇到错误,需要捕获错误并且执行
reject函数
try{
fn(resolve,reject)
} catch(e){
reject(e)
}
最后来实现then函数
- 首先判断两个参数是否为函数类型,因为两个参数是可选参数
- 当参数不是函数类型时,需要创建一个函数赋值给对应的参数,同时也实现了透传,比如以下代码
// 该代码目前在简单版中会报错 // 只是作为一个透传的例子 Promise.resolve(4).then().then((value) => console.log(value)) - 接下来就是一系列判断状态逻辑,当状态不是等待状态时,就是执行相对应的函数。如果状态是等待状态的话,就往回调函数中
push函数。
MyPromise.prototype.then = function (onFulfilled,onRejected) {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
以上就是简单版promise实现,接下来继续实现完整版Promise。
事件一个符合 Promise/A+ 规范的 Promise
首先先改造一下 resolve 和 reject 函数
- 对于
resolve函数来说,首先需要判断传入的值是否为Promise类型 - 为了保证函数执行顺序,需要将两个函数体代码使用
setTimeout包裹起来
function resolve(value){
if (value instanceof MyPromise) {
return value.then(resolve,reject)
}
setTimeout(() =>{
if(that.state === PENDING){
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map((cb) => cb(that.value))
}
},0)
}
function reject(value){
setTimeout(() =>{
if(that.state === PENDING){
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map((cb) => cb(that.value))
}
},0)
}
接下来继续改造then函数中的代码,首先我们需要新增一个变量promise2,因为每个then函数都需要返回一个新的Promise对象,该变量用于保存新的返回对象,我们先改造判断等待状态的逻辑
- 首先我们返回一个新的
Promise对象,并在Promise中传入一个函数 - 函数的基本逻辑和之前一样,往回调数组中
push函数 - 同样在执行函数的过程中可能会遇到错误,所以使用了
try...catch包裹 - 规范规定,执行
onFulfilled或者onRejected函数时会返回一个x,并且执行Promise解决过程,这是为了不同的Promise都可以兼容使用,比如 JQuery 的Promise能兼容ES6的Promise - 接下来改造判断执行状态的逻辑
- 这段代码和判断等待状态的逻辑基本一样,无非是传入的函数的函数体需要异步执行,这也是规范规定的
MyPromise.prototype.then = function (onFulfilled,onRejected) {
const that = this
let promise2
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r}
// resolutionProcedure 函数待实现
if (that.state === PENDING) {
return (promise2 = new MyPromise((resolve,reject) => {
that.resolvedCallbacks.push(() => {
try {
const x = onFulfilled(that.value)
resolutionProcedure(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
})
that.rejectedCallbacks.push(() => {
try {
const x = onRejected(that.value)
resolutionProcedure(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
})
}))
}
if (that.state === RESOLVED) {
return (promise2 = new MyPromise((resolve,reject) => {
setTimeout(() => {
try {
const x = onFulfilled(that.value)
resolutionProcedure(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
})
}))
}
if (that.state === REJECTED) {
return (promise2 = new MyPromise((resolve,reject) => {
setTimeout(() => {
try {
const x = onRejected(that.value)
resolutionProcedure(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
})
}))
}
}
最后,就是实现兼容各种Promise的resolutionProcedure 函数
- 首先规范规定了
x不能与promise2相等,这样会发生循环引用的问题 - 然后需要判断
x的类型 - 如果
x为Promise的话需要判断一下几个情况: - 1.如果
x处于等待状态,Promise需保持为等待状态直至x被执行或拒绝 - 2.如果
x处于其他状态,则用相同的值处理Promise - 当然以上这些是规范需要我们判断的情况,实际上我们不判断状态也是可行的。
- 接下来完成剩余代码
- 首先创建一个变量
called用于判断是否已经调用过函数 - 然后判断
x是否为对象或者函数,如果都不是的话,将x传入resolve中 - 如果
x是对象或者函数的话,先把x.then赋值给then,然后判断then的类型,如果不是函数类型的话,就将x传入resolve中 - 如果
then是函数类型的话,就将x作为函数的作用域this调用之,并且传递两个回调函数作为参数,第一参数叫做resolvePromise,第二个参数叫做rejectPromise,两个回调函数都需要判断是否已经执行过函数,然后进行相应的逻辑 - 以上代码在执行过程中如果抛出错误,错误将传入
reject函数中
function resolutionProcedure(promise2,x,resolve,reject){
if (promise2 === x) {
return reject(new TypeError('Error'))
}
if (x instanceof MyPromise) {
x.then(function (value) {
resolutionProcedure(promise2,value,resolve,reject)
})
}
let called = false
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x,(y) => {
if (called) return
called = true
resolutionProcedure(promise2,y,resolve, reject)
},
(e) => {
if (called) return
called = true
reject(e)
}
)
}
else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
}
else {
resolve(x)
}
}
以上就是符合 Promise/A+ 规范的实现了