手把手带你实现promise源码:培训班小张看后工资暴涨5k

Posted 前端呆头鹅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手带你实现promise源码:培训班小张看后工资暴涨5k相关的知识,希望对你有一定的参考价值。



本文较长,看后工资暴涨,建议抢先收藏。

一 主干部分

我们首先实现promise主干部分。

1.1 回调函数和状态管理

1 实现promise,就是实现一个类,接收一个立即执行的执行器(回调函数);

2 内含有三种状态,pending fulfilled rejected。状态确定不可更改;

3 含有resolve和reject函数用来更改状态。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class promiseDemo{
    constructor(fn){
        console.log(this.status)
        fn(this.resolve, this.reject)
        console.log(this.status)
    }
    status = PENDING
    resolve = () => {
        if(this.status !== PENDING) return
        this.status = FULFILLED
    }
    reject = () => {
        if(this.status !== PENDING) return
        this.status = REJECTED
    }
}

我们可以使用这个模拟类。

new promiseDemo((res, rej) => {
    console.log(1)
    res()
})

打印结果:

目前我们实现了回调函数执行和状态管理部分,接下来实现then的链式调用。

1.2 实现then函数

1 then定义在原型对象中,接受两个回调函数,根据状态的不同调用不同回调函数。

2 回调函数中接受参数,来自于调用resolve和reject时给出的参数。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class promiseDemo{
    constructor(fn){
        console.log(this.status)
        fn(this.resolve, this.reject)
        console.log(this.status)
    }
    status = PENDING
    value = undefined // 保存resolve调用时的参数
    reason = undefined // 保存reject调用时的参数
    resolve = value => {
        if(this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value // 保存参数等待then调用
    }
    reject = reason => {
        if(this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason // 保存参数等待then调用
    }
    then (successCallback, failCallback) { // 根据状态调用不同回调函数 为回调函数传入对应参数
        if(this.status === FULFILLED) {
            successCallback(this.value)
        }else if(this.status === REJECTED) {
            failCallback(this.reason)
        }
    }
}

现在我们可以在实例后添加then,根据状态执行对应回调,并在回调函数中使用resolve/reject中传递的参数。

new promiseDemo((res, rej) => {
    res(666)
}).then(res=>{
    console.log('res', res)
}, rej=>{
    console.log('rej', rej)
})

打印结果:

二 异步逻辑添加

当加入异步逻辑(如定时器),进入then时状态为pending,这时先将回调函数存储起来,当对应状态改变时调用。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class promiseDemo{
    constructor(fn){
        console.log(this.status)
        fn(this.resolve, this.reject)
        console.log(this.status)
    }
    ...
    failCailback = undefined
    successCallback = undefined
    resolve = value => {
        ...
        this.successCallback && this.successCallback(this.value) // 执行存储的回调
    }
    reject = reason => {
        ...
        this.failCallback && this.failCallback(this.reason) // 执行存储的回调
    }
    then (successCallback, failCallback) {
        if(this.status === FULFILLED) {
            successCallback(this.value)
        }else if(this.status === REJECTED) {
            failCallback(this.reason)
        }else { // 如果状态为pending 则将回调函数存储
            this.failCailback = failCallback
            this.successCallback = successCallback
        }
    }
}

调用如下:

new promiseDemo((res, rej) => {
    setTimeout(() => {
        console.log('2秒后')
        res(666)
    }, 2000)
}).then(res => {
    console.log('res', res)
}, rej=>{
    console.log('rej', rej)
})

打印结果:

三 then的链式调用

使then方法返回promise对象,就可以链式调用。

在then方法中创建promise对象实例并返回。

3.1 返回简单值

下面做一个链式调用的简单实现:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class promiseDemo{
    ...
    then (successCallback, failCallback) {
        // 创建promise对象实例并返回
        let proRes = new promiseDemo((res, rej) => {
            // 逻辑放到这个位置是为了使用res/rej抛出结果 使后续then可以捕捉状态和参数
            if(this.status === FULFILLED) {
              	// 捕捉返回值 抛出结果
                let data = successCallback(this.value)
                // res做了三件事 改变状态 记录参数 最后执行等待中的回调
                res(data)
            }else if(this.status === REJECTED) {
                failCallback(this.reason)
            }else {
                this.failCailback = failCallback
                this.successCallback = successCallback
            }
        })
        return proRes
    }
}

上面的代码中,对于在回调successCallback中给出了返回值的情况做了处理,让后续then可以捕获一个成功状态,值为返回的参数。

调用如下:

let a = new promiseDemo((res, rej) => {
    res(666)
}).then(res => {
    console.log('res', res)
    return 200
}, rej=>{
    console.log('rej', rej)
}).then(res => {
    console.log('res', res)
}, rej=>{
    console.log('rej', rej)
})

打印结果:

3.2 返回promise对象

实际使用中还有另一种情况,将是回调并不是直接返回值,而是返回一个promise对象

这里我们判断一个值是否是promise对象。

这个函数中res与rej函数改变的是当前then中新建立的promise对象走向,也是传递到后续then的状态走向。

这里做了一个回调函数中返回的promise对象状态到then中promise对象的状态同步。

function resolvePromise (x, res, rej) {
    if(x instanceof promiseDemo){
        x.then(value => res(value), reason => rej(reason))
        // x.then(res, rej)
    }else {
        res(x)
    }
}
class promiseDemo{
    ...
    then (successCallback, failCallback) {
        let proRes = new promiseDemo((res, rej) => {
            if(this.status === FULFILLED) {
                let data = successCallback(this.value)
                // console.log(data)
                // 这里调用了上面定义的函数 值是否promise对象逻辑分支
                resolvePromise(data, res, rej)
            }else if(this.status === REJECTED) {
                failCallback(this.reason)
            }else {
                this.failCailback = failCallback
                this.successCallback = successCallback
            }
        })
        return proRes
    }
}

调用如下:

let promise2 = new promiseDemo((res, rej) => {
    res(200)
})

let a = new promiseDemo((res, rej) => {
    res(666)
}).then(res => {
    console.log('res1', res)
    return promise2
}, rej=>{
    console.log('rej', rej)
}).then(res => {
    console.log('res2', res)
}, rej=>{
    console.log('rej', rej)
})

打印结果:

3.3 默认参数

在then中传递的参数是可选的,当then中没有对应处理函数时,状态会自动向后传递。

promise
.then()
.then(value => console.log(value))
// ===
promise
.then(value => value)
.then(value => console.log(value))

这种效果也很容易实现,给一个默认值即可。

class promiseDemo{
    ...
    then (successCallback = value => value, failCallback = value => value) {
        ...
        return proRes
    }
}

调用代码改为:

let a = new promiseDemo((res, rej) => {
    res(666)
}).then(res => {
    console.log('res1', res)
    return promise2
}, rej=>{
    console.log('rej', rej)
})
.then()
.then(res=>{
    console.log('res2', res)
})

这种情况下,修改前的代码会报错误:

successCallback is not function

修改后的代码则会正常运行,将状态及参数后传。

打印结果:

四 错误捕获

4.1 执行器错误捕获

当执行器(promise回调函数)运行报错,应当被catch函数捕获。这是怎么实现的呢。

这里我们在回调函数中抛出一个错误。

let a = new promiseDemo((res, rej) => {
    throw new Error('executor error')
    res(666)
}).then(res => {
    console.log('res', rej)
},rej => {
    console.log('rej', rej)
})

在promise中捕捉该错误并抛到rej中,以改变状态。

class promiseDemo{
    constructor(fn){
        try{
        fn(this.resolve, this.reject)
        }catch(message){
            this.reject(message)
        }
    }
    ...
}

错误就被捕捉并抛到then对应的回调中了。

打印结果:

4.2 then回调错误捕获

当执行then回调时产生的错误也应当被捕获。

class promiseDemo{
		...
    then (successCallback = value => value, failCallback = value => value) {
        let proRes = new promiseDemo((res, rej) => {
            if(this.status === FULFILLED) {
                try {
                    let data = successCallback(this.value)
                    resolvePromise(data, res, rej)
                }catch(message) {
                    rej(message) // 改变当前新的promise对象状态
                }
            }else if(this.status === REJECTED) {
                failCallback(this.reason)
            }else {
                this.failCailback = failCallback
                this.successCallback = successCallback
            }
        })
        return proRes //返回新的promise对象
    }
}

尝试在then的回调中抛出一个错误。

let a = new promiseDemo((res, rej) => {
    res(666)
}).then(res => {
    console.log('res', rej)
    throw new Error('executor error')
},rej => {
    console.log('rej', rej)
}).then(res => {
    console.log('res2', rej)
},rej => {
    console.log('rej2', rej)
})

打印结果:

刚刚我们都是在同步成功回调中添加的内容,下面补全不同状态下的处理方式。

class promiseDemo{
    ...
    then (successCallback = value => value, failCallback = value => value) {
        let proRes = new promiseDemo((res, rej) => {
            if(this.status === FULFILLED) {
                try {
                    let data = successCallback(this.value)
                    resolvePromise(data, res, rej)
                }catch(message) {
                    rej(message)
                }
            }else if(this.status === REJECTED) {
                try {
                    let data = failCallback(this.reason)
                    resolvePromise(data, res, rej)
                }catch(message) {
                    rej(message)
                }
            }else {
                this.successCailback = () 手把手带你实现符合Promise/A+规范的Promise

深入mybatis源码解读~手把手带你debug分析源码

#yyds干货盘点#学不懂Netty?看不懂源码?不存在的,这篇文章手把手带你阅读Netty源码

手把手带你阅读Mybatis源码缓存篇

手把手带你分析LeanCancary源码

手把手带你源码解析HDFS文件上传之create创建过程