(一)基本框架搭建
向promise中传入一个函数executor,executor接受resolve和reject两种参数。在new Promise时,executor就会自动调用。
通过es6的class进行手写promise,对应的myPromise类如下:
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)()
}
resolve(result) {
}
reject(result) {
}
}
继续下一步之前一定要搞懂promise的特点:
promise具有3种状态:pending(待定)、fulfilled(成功)、rejected(失败)
初始状态为pending,得到resolve或reject函数调用后,状态变为fulfilled或rejected,这个状态是不可逆的
class MyPromise {
constructor(executor) {
// 设置初始状态 pending待定
this.status = 'pending'
// 设置初始结果为空,resolve或reject后才会变为result或error
this.result = null
executor(this.resolve,this.reject)
}
resolve(result) {
// 只能从pending=>fulfilled
if (this.status === 'pending') {
this.status = 'fulfilled'
this.result = result
}
}
reject(result) {
// 只能从pending=>rejected
if (this.status === 'pending') {
this.status = 'rejected'
this.result = result
}
}
}
let p = new MyPromise((resolve, reject) => {
resolve('11') // 报错this.status is undefined
})
报错this.status is undefined原因:
resolve是MyPromise及其实例对象的内部方法,直接调用resolve(),会导致this的指向不再是p这个实例对象,需要使用bind方法重新绑定this指向
// 使用bind重新绑定this指向
executor(this.resolve.bind(this),this.reject.bind(this))
(二)then方法的实现
1.基本实现
.then的第一个参数是一个函数,该函数将在 promise resolved 且接收到结果后执行。
.then的第二个参数是一个函数,该函数将在 promise rejected 且接收到 error 信息后执行。
MyPromise类中的then方法接受两个回调函数onfulfilled和onrejected,判断该实例的状态,成功则执行onfulfilled,失败则执行onrejected
class MyPromise {
constructor(executor) {
// 设置初始状态 pending待定
this.status = 'pending'
// 设置初始结果为空,resolve或reject后才会变为result或error
this.result = null
// 使用bind重新绑定this指向
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
// 只能从pending=>fulfilled
if (this.status === 'pending') {
this.status = 'fulfilled'
this.result = result
}
}
reject(result) {
// 只能从pending=>rejected
if (this.status === 'pending') {
this.status = 'rejected'
this.result = result
}
}
then(onfulfilled, onrejected) {
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
onfulfilled(this.result)
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
onrejected(this.result)
}
}
}
let p = new MyPromise((resolve, reject) => {
// resolve是MyPromise及其实例对象的内部方法 直接调用resolve()会报错,需要使用bind重新绑定this指向
resolve('11')
})
p.then((result) => {
console.log(result);
}, (error) => {
console.log(error.message);
})
2.异常处理优化
(1)then函数传入非函数类型情况
当then方法没有传入resolve的回调函数,而是传入一个undefined时,Promise并不会报错
而对应过来向MyPromise的then方法传入一个非函数的数据就会导致onfulfilled(this.result)报错
因此在执行函数操作之前,先判断传入的是否为函数类型,不是则转换为空函数
then(onfulfilled, onrejected) {
// 当传入的onfulfilled或onrejected为非函数类型时转换为空函数,防止报错
if( typeof onfulfilled !=='function'){
onfulfilled = ()=>{}
}
if(typeof onrejected !== 'function'){
onrejected = ()=>{}
}
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
onfulfilled(this.result)
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
onrejected(this.result)
}
}
(2)在executor函数中直接抛出错误,不执行resolve情况
Promise能够在then的第二个回调函数中接收到error
在MyPromise中,then函数并不能接收到error,因为executor函数中接收到error后没有对错误进行处理就直接抛出了,因此使用try catch接住错误,再对错误进行处理即可
// 如果在executor中抛出了错误,就用try catch接收,再执行resolve函数
try {
// 使用bind重新绑定this指向
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
3.实现then()的异步性
在Promise中,then方法是异步执行的(为了更好的解决回调地狱的问题),而在MyPromise中的then方法还是同步执行的,setTimeOut是一个异步执行函数,因此我们可以使用setTimeOut()来模拟then()的异步执行
then(onfulfilled, onrejected) {
// 当传入的onfulfilled或onrejected为非函数类型时转换为空函数,防止报错
if (typeof onfulfilled !== 'function') {
onfulfilled = () => { }
}
if (typeof onrejected !== 'function') {
onrejected = () => { }
}
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
// 模拟异步 在所有同步任务执行完后才会执行异步任务
setTimeout(() => {
onfulfilled(this.result)
})
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
setTimeout(() => {
onrejected(this.result)
})
}
}
4.异步任务的执行
在Promise中执行异步任务,输出顺序为: 1 2 4 3 成功
而对应过来的MyPromise的输出顺序为:1 2 4 3,但并没有执行then的成功回调
这是因为异步任务要在同步任务执行之后才会执行,因此resolve()要在then()后面执行,在then()执行时,实例p的状态仍为pending,无法执行成功回调,而在return执行完毕后实例p的status才改变成fulfilled
解决办法:将onfulfilled和onrejected回调函数保存起来,在resolve()或reject()调用时再调用对应函数
这里使用数组保存回调函数,还解决了一个问题:多次调用.then(),保存回调的数组会按照执行顺序依次存入异步回调函数,在同步操作进行完之后依次执行。
class MyPromise {
constructor(executor) {
// 设置初始状态 pending待定
this.status = 'pending'
// 设置初始结果为空,resolve或reject后才会变为result或error
this.result = null
// 分别保存一个onfulfilled和onrejected的回调函数数组
this.resolveCallback = []
this.rejectCallback = []
// 如果在executor中抛出了错误,就用try catch接收,再执行resolve函数
try {
// 使用bind重新绑定this指向
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
// 只能从pending=>fulfilled
if (this.status === 'pending') {
this.status = 'fulfilled'
this.result = result
// 异步 执行onfulfilled回调
this.resolveCallback.forEach((callback) => {
callback(result)
})
}
}
reject(result) {
// 只能从pending=>rejected
if (this.status === 'pending') {
this.status = 'rejected'
this.result = result
// 异步 执行onrejected回调
this.rejectCallback.forEach((callback) => {
callback(result)
})
}
}
then(onfulfilled, onrejected) {
// 当传入的onfulfilled或onrejected为非函数类型时转换为空函数,防止报错
if (typeof onfulfilled !== 'function') {
onfulfilled = () => { }
}
if (typeof onrejected !== 'function') {
onrejected = () => { }
}
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
// 模拟异步 在所有同步任务执行完后才会执行异步任务
setTimeout(() => {
onfulfilled(this.result)
})
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
setTimeout(() => {
onrejected(this.result)
})
}
// 状态为pending 将回调保存到数组中 后续再执行
if (this.status === 'pending') {
this.resolveCallback.push(onfulfilled)
this.rejectCallback.push(onrejected)
}
}
}
console.log(1);
let p = new MyPromise((resolve, reject) => {
// resolve是MyPromise及其实例对象的内部方法 直接调用resolve()会报错,需要使用bind重新绑定this指向
// resolve('11')
// throw new Error('抛出一个异常')
console.log(2);
setTimeout(() => {
console.log(3);
resolve('成功')
}, 1000)
})
p.then((result) => {
console.log(result);
}, (error) => {
console.log(error.message);
})
console.log(4);
5.Promise的链式调用
.then(handler) 中所使用的处理程序(handler)可以创建并返回一个 promise
在这种情况下,其他的处理程序将等待它 settled 后再获得其结果
首先,为了实现then()的链式调用,让then()的返回值为new MyPromise,返回值就能继续调用then方法了;
其次,为了实现以return的值作为新的MyPromise的result,将resolve回调内resolve的返回值保存起来并resolve给新的MyPromise。(有点绕有点绕,我自己都有点晕了)
then(onfulfilled, onrejected) {
// 当传入的onfulfilled或onrejected为非函数类型时转换为空函数,防止报错
if (typeof onfulfilled !== 'function') {
onfulfilled = () => { }
}
if (typeof onrejected !== 'function') {
onrejected = () => { }
}
// then()返回新的promise实例 即可实现then的链式调用
return new MyPromise((resolve) => {
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
// 模拟异步 在所有同步任务执行完后才会执行异步任务
setTimeout(() => {
// 用于接收回调函数返回的数据 并作为新的promise实例的result
let x = onfulfilled(this.result)
// 如果x是MyPromise实例就会导致输出的结果是MyPromise
resolve(x)
})
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
setTimeout(() => {
onrejected(this.result)
})
}
// 状态为pending 将回调保存到数组中 后续再执行
if (this.status === 'pending') {
this.resolveCallback.push(onfulfilled)
this.rejectCallback.push(onrejected)
}
})
}
若成功回调里返回的值是一个MyPromise对象或者叠加很多层的MyPromise对象,那么直接resolve(x)输出的值就是一个MyPromise对象
p.then((result) => {
console.log(result);
return new MyPromise((resolve, reject) => {
resolve(666)
})
}).then((result) => {
console.log(result);
})
对应过来Promise输出的值直接就是666,就算是嵌套多层Promise对象也一样
因此,在resolve(x)之前,要先判断x是一个数据还是MyPromise实例对象,这里可以另外写一个判断的函数resolvePromise()来辅助
resolvePromise()中,设置递归调用来防止获取不到叠了很多层的Mypromise对象的result
then(onfulfilled, onrejected) {
// 当传入的onfulfilled或onrejected为非函数类型时转换为空函数,防止报错
if (typeof onfulfilled !== 'function') {
onfulfilled = () => { }
}
if (typeof onrejected !== 'function') {
onrejected = () => { }
}
// then()返回新的promise实例 即可实现then的链式调用
return new MyPromise((resolve, reject) => {
// 状态为fulfilled就执行onfulfilled
if (this.status === 'fulfilled') {
// 模拟异步 在所有同步任务执行完后才会执行异步任务
setTimeout(() => {
// 用于接收回调函数返回的数据 并作为新的promise实例的result
let x = onfulfilled(this.result)
this.resolvePromise(x, resolve, reject)
})
}
// 状态为rejected九执行onrejected
if (this.status === 'rejected') {
setTimeout(() => {
onrejected(this.result)
})
}
// 状态为pending 将回调保存到数组中 后续再执行
if (this.status === 'pending') {
this.resolveCallback.push(onfulfilled)
this.rejectCallback.push(onrejected)
}
})
}
resolvePromise(x, resolve, reject) {
// x是MyPromise实例对象
if (x instanceof MyPromise) {
x.then(result => {
// 递归 防止多层嵌套MyPromise 一层层拨开MyPromise
this.resolvePromise(result, resolve, reject)
}, error => {
reject(error)
})
} else {
resolve(x)
}
}
异步的链式调用
此时的MyPromise若进行一个异步操作,异步执行的只是回调数组里的onfulfilled(),而resolvePromise()并没有实现,后续.then()的调用也就没有后续了。
因此将返回结果和返回结果的resolve()包裹成一个函数,并加入到数组中,后续再进行异步调用即可。
// 状态为pending 将回调保存到数组中 后续再执行
if (this.status === 'pending') {
// 将这两行代码包裹为一个函数 即可实现异步链式调用
this.resolveCallback.push(() => {
let x = onfulfilled(this.result)
this.resolvePromise(x, resolve, reject)
})
this.rejectCallback.push(onrejected)
}
接下来进行一个测试:
let p = new MyPromise((resolve, reject) => {
// 异步
setTimeout(() => {
resolve(1)
}, 10)
})
// 链式调用
p.then((result) => {
console.log(result);
return result * 2
}).then((result) => {
console.log(result);
return result * 2
}).then((result) => {
console.log(result);
return result * 2
}).then(result => {
console.log(result);
})
结果:
(三) 未完待续
欧了欧了,基本的promise功能就已经实现了,接下来的all()、race()、catch()、finally()啦等等都是异曲同工之妙,有闲工夫的话就补补 ~w~