Promise:手撕promise

(一)基本框架搭建

向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~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值