Promise源码实现

48 阅读4分钟

一、Promise基本实现

1、promise是一个类,无需考虑兼容性

2、当使用promise的时候,会传入一个执行器,此执行器是立刻执行

3、当前excutor 给了两个函数可以来描述当前promise的状态,promise中有三个状态成功态,失败态,等待态

默认为等待态,如果调用resolve会走到成功态,如果调用reject或者发生异常,会走向失败态

4、每个promise实例都有一个then方法

5、promise 一旦状态变化后不能更改

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class Promise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value
                this.status = FULFILLED
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
            }
        }
         try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}

module.exports = Promise

二、Promise链式调用

当用户调用then方法的时候,此时promise可能为等待态,先暂存起来,因为后续可能会调用resolvereject,等会再触发对应onFulfilled或者onRejected

1、promise的链式调用:当调用then方法后会返回一个新的promise

情况1:then中方法放回的是一个(普通值,不是promise)的情况,会作为外层下一次then的成功结果

情况2:then中方法执行出错,会走到外层下一次then的失败结果

情况3:如果then中方法返回的是一个promise对象,此时会根据promise的结果来处理是走成功还是失败(传入的是成功或者是失败的内容)

tip:无论上一次then走的是成功还是失败,只要返回的是普通值,都会执行下一次then的成功

总结:如果返回一个普通值(除了promise)就会传递给下一个then的成功,如果返回一个失败的promise或者抛出异常,会走下一个then的失败

2、then处理核心resolvePromise()

1、判断x是否和promise2相同,防止循环调用等待

let promise2 = new Promise((resolve, reject) => {
    resolve(1)
}).then(() => {
    return promise2
})

2、判断x是否为对象或者函数:

2.1不是,说明x为普通值,直接将他放到promise2.resolve中

2.2是,取出then=x.then,有可能then方法是通过defineProperty实现的,取值时可能发生异常

2.2.1判断then类型是否为function:

是:说明是promise,执行then.call(x)(x.then这样写可能会触发getter,可能会发生异常)

let p = {}
let index = 0
Object.defineProperties(p, 'then', {
    get() {
        if (++index == 2) throw new Error
    }
})
p.then
p.then

否:说明可能为{}、{then:{}},直接resolve(x)

2.3设置caller防止其他人写的promise在调用后也发生异常

2.4处理then穿透问题

new Promise((resolve) => {
    resolve(200)
}).then().then().then().then((data) => {
    console.log(data, 's')
}, err => {
    console.log(err, 'e')
})
//console结果 200s

then(onFulfilled, onRejected)应该为可选参数,如果未使用,应该将参数继续传递下去 处理方法:如果then中的onFulfilledonRejected不是一个function,需要被忽略,直接返回值即可

onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : v => v
onRejected = typeof onRejected == 'function' ? onRejected : err => { throw err }
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

//利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
    //核心流程
    if (promise2 === x) {
        return reject(new TypeError('错误'))
    }
    //考虑和其他人写的promise兼容
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called = false
        try { //then()可能由defineProperty实现,可能发生异常
            let then = x.then
            if (typeof then === 'function') {
                //为promise,x.then 可能触发getter引起异常
                then.call(x, y => {
                    if (called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) return
                    called = true
                    reject(r)
                })
            } else { //{} {then:{}}
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x) //说明返回的是一个普通值,直接放到promise2.resolve中
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = []  //成功回调方法
        this.onRejectedCallbacks = []  //失败回调方法
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value
                this.status = FULFILLED //修改状态

                this.onResolvedCallbacks.forEach(fn => fn())

            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED

                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
    then(onFulfilled, onRejected) {

        //用于实现链式调用
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {//成功调用成功方法
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === REJECTED) {//失败调用失败方法
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === PENDING) {  //代码是异步调用resolve或者reject
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // to do...
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            //to do...
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2


    }
}



module.exports = Promise