前言
想必大家对于promise的使用已经很熟练了,接下来就手写个promise吧。
promise的三种状态
接下来看一段代码
const promise1 = new Promise((resolve, reject) => {})
const promise2 = new Promise((resolve, reject) => {
resolve('aaa')
reject('bbb)
})
const promise3 = new Promise((resolve, reject) => {
reject('error')
})
这段代码的执行结果是
不难发现,promise有三种状态。
- promise的初始状态为 pending,
- 执行resolve之后状态变为 fulfilled
- 执行reject之后状态变为 rejected
- 当状态发生转变后,不可再次转变
实现resolve,reject
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this.status = 'pending' // promise默认状态
this.value = undefined
this.resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
this.reject = err => {
if (this.status === PENDING) {
this.status = REJECTED
this.value = err
}
}
// 在new proimse时会传入一个函数,接受两个参数,并立即执行
executor(this.resolve, this.reject)
}
}
简单的resolve,reject就实现了,看看效果吧
const promise = new MyPromise((resolve, reject) => {
resolve('aaa')
reject('error message')
})
console.log(promise)
结果如下
可以发现,当我们在resolve之后执行reject,promise的状态并没有改变,达到我们的目的。可是我们怎么拿到resolve或reject的值呢?一般是通过then函数来获取promise值,接下来就实现一个简单的then函数吧。
实现then
- 首先then接受两个函数参数
- 第一个参数是当promise状态为fulfilled时执行回调
- 第二个参数是当promise状态为rejected时执行回调
then(onFulFilled, onRejected) {
if (this.status === PENDING) {
} else if (this.status === FULFILLED) {
onFulFilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.value)
}
}
测试一下吧
const promise = new MyPromise((resolve, reject) => {
resolve('aaa')
reject('error message')
})
promise.then(
res => {
console.log('success:', res) // 当状态为fulfilled时,打印
},
err => {
console.log('err:', err) // 当状态为rejected时,打印
}
)
我们经常在promise中进行异步操作,如ajax请求。但是then函数是同步进行的,当then函数执行时,promise的状态还是pending。要想在promise状态改变时执行then传入的函数,我们要在pending装填时保存then传入的函数,等异步代码执行的时候,再执行保存的函数。
class MyPromise {
constructor(executor) {
this.status = 'pending' // promise默认状态
this.value = undefined
// 保存then传入的函数
this.onFulFilledFns = []
this.onRejectedFns = []
this.resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFulFilledFns.forEach(fn => {
fn(this.value)
})
}
}
this.reject = err => {
if (this.status === PENDING) {
this.status = REJECTED
this.value = err
this.onRejectedFns.forEach(fn => {
fn(this.value)
})
}
}
// 在new proimse时会传入一个函数,接受两个参数,并立即执行
executor(this.resolve, this.reject)
}
then(onFulFilled, onRejected) {
if (this.status === PENDING) {
this.onFulFilledFns.push(onFulFilled)
this.onRejectedFns.push(onRejected)
} else if (this.status === FULFILLED) {
onFulFilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.value)
}
}
}
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => { // 这里用setTimeout代替异步请求
resolve('aaa')
}, 1000)
})
promise.then(
res => {
console.log('success:', res) // 一秒后成功执行
},
err => {
console.log('err:', err)
}
)
好了,普通的then调用已经实现了,但是,这与我们平时用的promise不一样啊。平时都是直接.then,可以链式调用,那接下来就分析一下怎么实现then的链式调用。
then的链式调用(核心)
根据我们平时使用promise,我们可以发现以下功能
- 调用then时返回一个新的promise对象
- 在resoolve或者reject中返回一个变量时,则会把这个值包装成promise,然后resolve出去,在下一个then回调中第一个函数就可以获取到。 好了,看下面代码
then(onFulFilled, onRejected) {
// if (this.status === PENDING) {
// this.onFulFilledFns.push(onFulFilled)
// this.onRejectedFns.push(onRejected)
// } else if (this.status === FULFILLED) {
// // fulfilled时
// onFulFilled(this.value)
// } else if (this.status === REJECTED) {
// onRejected(this.value)
// }
// 发现上面代码和return出的promise代码重复,而且在new promise的时候,也会执行传入的函数
return new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 如果then没有返回值,则直接是resolve(undefined)
// 如果有返回值,则拿到then的第一个函数的返回值,然后resolve出去
const value = onFulFilled(this.value) // onFulfilled没有返回值时,value为undefined
resolve(value)
} else if (this.status === REJECTED) {
const error = onRejected(this.value)
// 在rejected中的返回值,用resolve传递状态
resolve(error)
} else if (this.status === PENDING) {
// 如果在new promise中,使用的是异步调用呢,promise的状态改变是异步的
// then的回调时同步的
// 我们要怎么拿到异步之后resolve或者reject的值呢?
// 将回调函数存起来?如果函数有返回值,怎么拿到保存函数的返回值呢?我们可以给保存的函数再包一层函数,利用闭包来解决问题
this.onFulFilledFns.push(() => {
const value = onFulFilled(this.value)
resolve(value)
})
this.onRejectedFns.push(() => {
const error = onRejected(this.value)
reject(error)
})
}
})
}
写个例子试试吧
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
})
.then(
res => {
console.log('res1:', res) // 成功打印
return 'bbb'
},
err => {
console.log('err1:', err)
}
)
.then(
res => {
console.log('res2:', res) // 成功打印
},
err => {
console.log('err2:', err)
}
)
但是我们没有做异常捕获,我们再优化一下代码
// 工具函数
function executorFnWhenCatchError(fn, resolve, reject, value) {
try {
const res = fn(value)
resolve(res)
} catch (error) {
reject(error)
}
}
...
then(onFulFilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 如果then没有返回值,则直接是resolve(undefined)
// 如果有返回值,则拿到then的第一个函数的返回值,然后resolve出去
// onFulfilled没有返回值时,value为undefined
executorFnWhenCatchError(onFulFilled, resolve, reject, this.value)
} else if (this.status === REJECTED) {
// 在rejected中的返回值,用resolve传递状态
executorFnWhenCatchError(onRejected, resolve, reject, this.value)
} else if (this.status === PENDING) {
// 如果在new promise中,使用的是异步调用呢,promise的状态改变是异步的
// then的回调时同步的
// 我们要怎么拿到异步之后resolve或者reject的值呢?
// 将回调函数存起来?怎么拿到保存函数的返回值呢?我们可以给保存的函数包一层函数
this.onFulFilledFns.push(() => {
executorFnWhenCatchError(onFulFilled, resolve, reject, this.value)
})
this.onRejectedFns.push(() => {
executorFnWhenCatchError(onRejected, resolve, reject, this.value)
})
}
})
}
我们抛出个异常试试
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
})
.then(
res => {
console.log('res1:', res)
throw new Error('error message')
},
err => {
console.log('err1:', err)
}
)
.then(
res => {
console.log('res2:', res)
},
err => {
console.log('err2:', err) // 成功捕获到异常
}
)
实现catch
实现then的回到之后,catch就很简单了。
- catch传入一个函数,当捕获到错误时,如果没有then没有传入第二个参数,会执行catch回调函数
- catch后会返回一个promise
我们上面实现的then就是返回一个promise,而且也可以捕获异常,那么catch可以这样实现
catch(onRejected) {
return this.then(undefined, onRejected)
}
看着是不是很简单,我们来试一下
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
})
.then(
res => {
console.log('res1:', res)
throw new Error('error message')
},
err => {
console.log('err1:', err)
}
)
.then(res => {
console.log('res2:', res)
})
.catch(err => {
console.log('err2:', err)
})
不幸,报错了,fn不是一个函数。不难发现,我们catch前面的then回调中没有传入对异常的捕获函数。则在第一个then回调中抛出异常,则会遍历执行onRejectedFns里的函数,遇到undefined不是一个函数,即报错。为了解决这个问题,我们可以给错误回调一个默认值。
then(onFulFilled, onRejected) {
// 如果传入的onRejected函数为空,我们要想传递报错,则给默认函数,在默认函数中传递错误
onRejected =
onRejected ||
(err => {
throw new Error(err)
})
return new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 如果then没有返回值,则直接是resolve(undefined)
// 如果有返回值,则拿到then的第一个函数的返回值,然后resolve出去
// onFulfilled没有返回值时,value为undefined
executorFnWhenCatchError(onFulFilled, resolve, reject, this.value)
} else if (this.status === REJECTED) {
// 在rejected中的返回值,用resolve传递状态
executorFnWhenCatchError(onRejected, resolve, reject, this.value)
} else if (this.status === PENDING) {
// 如果在new promise中,使用的是异步调用呢,promise的状态改变是异步的
// then的回调时同步的
// 我们要怎么拿到异步之后resolve或者reject的值呢?
// 将回调函数存起来?怎么拿到保存函数的返回值呢?我们可以给保存的函数包一层函数
this.onFulFilledFns.push(() => {
executorFnWhenCatchError(onFulFilled, resolve, reject, this.value)
})
this.onRejectedFns.push(() => {
executorFnWhenCatchError(onRejected, resolve, reject, this.value)
})
}
})
}
好了catch就实现了
实现finally
在我们平时的使用过程中,finally不论是否成功都会执行。很简单,我们在then中传入两个相同的函数就可以了,这样不论成功还是失败,都会执行finally的回调函数。
finally(onFulFilled) {
this.then(onFulFilled, onFulFilled)
}
还是会出现和catch一样的问题。我们在掉用catch的时候,传入的onFulFilled的为undefined,很简单,我们给onFulFilled函数一个默认值。
onFulFilled =
onFulFilled ||
(value => {
return value
})
测试一下
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
})
.then(
res => {
console.log('res1:', res)
throw new Error('error message')
},
err => {
console.log('err1:', err)
}
)
.then(res => {
console.log('res2:', res)
})
.catch(err => {
console.log('err2:', err)
})
.finally(() => {
console.log('finally') // 成功执行
})
实现静态方法resolve和reject
promise类中的静态方法,返回一个promise
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value)
})
}
实现race
实现上面的功能后,race就很简单了
- race接收一个promise数组,当其中有一个promise状态发生变化时,返回这个变化的promise值
- 通过then回调,拿到值
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(
res => {
resolve(res)
},
err => {
resolve(err)
}
)
})
})
}
测试一下
const promsie1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 3000)
})
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('bbb')
}, 2000)
})
MyPromise.race([promsie1, promise2]).then(res => {
console.log(res) // 成功执行 输出bbb
})
实现all
- 接收promise数组参数
- 当所有promise状态都变成fulfilled返回所有状态和对应的值
- 如果有一个promise的状态变成rejected,只返回这个错误的promise
static all(promises) {
const data = []
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(
res => {
data.push({ statu: 'fulfilled', value: res })
if (data.length === promises.length) {
resolve(data)
}
},
err => {
resolve({ statu: 'rejected', value: err })
}
)
})
})
}
测试一下
const promsie1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 3000)
})
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('bbb')
}, 2000)
})
MyPromise.all([promsie1, promise2]).then(res => {
console.log(res) // 符合
})
实现any
- 接收promise数组参数
- 当有一个有promise状态变为fulfilled,返回改状态和对应的值
- 如果promise的状态全部都是rejected,则抛出错误
static any(promises) {
const data = []
let index = 0
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(
res => {
data.push({ statu: 'fulfilled', value: res })
if (data.length === promises.length) {
resolve(data)
}
},
err => {
data.push({ statu: 'rejected', value: err })
index++
if (index === promises.length) {
resolve({ err: 'all promise is rejected' })
}
if (data.length === promises.length) {
resolve(data)
}
}
)
})
})
}
测试一下
const promsie1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('aaa')
}, 2000)
})
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('bbb')
}, 1000)
})
MyPromise.any([promsie1, promise2]).then(res => {
console.log(res) // 成功报错
})
实现allSettled
- 接收promise数组参数
- 将所有的promise状态和结果放到数组里返回
static allSettled(promises) {
const data = []
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(
res => {
data.push({ statu: 'fulfilled', value: res })
if (data.length === promises.length) {
resolve(data)
}
},
err => {
data.push({ statu: 'rejected', value: res })
if (data.length === promises.length) {
resolve(data)
}
}
)
})
})
}
测试一下
const promsie1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 2000)
})
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('bbb')
}, 1000)
})
MyPromise.allSettled([promsie1, promise2]).then(res => {
console.log(res) // 成功返回
})
此篇总结于coderwhy老师的js高级课程。希望你看完这篇文章,对js函数回调掌握恐怖如斯。