手写 Promise 详细解读

551 阅读4分钟

用法 😏

let p = new Promise((resolve, reject) => {
    if (success) resolve('ok')
    else reject('error')
})

p.then(
    res => console.log('成功', res),
    err => console.log('失败', err)
    )
    .catch(err => console.log('失败', err))
    .finally(() => console.log('无论成功失败都执行'))
    
Promise.resolve('ok')
Promise.reject('error')

Promise.all([p1, p2, p3]).then(res => {
    // res 每个 promise 的结果组成的数组
})

Promise.race([p1, p2, p3]).then(res => {
    // 最早完成的 promise 的结果
})

原理 😊

  • Promise 的状态只能从 pending => resolved 或 从 pending => rejected,状态一旦改变不会再变
  • 要创建一个 Promise对象,需要new Promise(executor),并传入一个 executor,该函数默认有两个方法参数(resolve, reject),在executor内执行用户逻辑(异步或同步代码),执行完根据成功/失败情况调用resolve/reject
  • Promise会根据Promise对象当前的状态决定走.then中的 onFullfilled/onRejected(用户传入的成功处理函数、失败处理函数)
  • Promise对象可以无限.then则代表每次返回的都是一个新的Promise对象,每个.then函数返回的data都会作为下一个.then函数的参数

基本实现 👩🏻‍💻

const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        // resolve 方法,将 Promise 对象的状态改变为成功,并将成功值赋给 value 供给 .then方法的 onFullfilled 使用
        let resolve = (value) => {
            if (this.status === PENDING) {
                this.status = RESOLVED;
                this.value = value;
            }
        }
        // reject 方法,将 Promise 对象的状态改变未失败,并将失败值赋给 reason 供给 .then 方法的 onRejected 使用
        let reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
            }
        }
        // 捕获同步代码产生的错误
        try {
            executor(resolve, reject);
        } catch(e) {
            reject(err)
        }
    }
    // Promise 实例的 then 方法
    then(onFullfilled, onRejected) {
        // 如果状态是 成功态,执行 成功的回调
        if (this.status === RESOLVED) {
            onFullfilled(this.value)
        }
        // 如果状态是 失败态,执行 失败的回调
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}

到这里实现了最基本的状态变更、数据存储及成功或失败的回调调用,使用一下

let p = new Promise((resolve, reject) => {
    resolve('ok')
})
p.then(res => console.log('成功', res)) // 成功ok

加强 🔨

Promise 中的 executor 支持异步逻辑,即resolve/reject并不是立即调用

// 增加异步时回调的存储
class Promise {
    constructor(props) {
        // ...
        this.onFullfilledCallbacks = []; // 存储成功回调
        this.onRejectedCallbacks = []; // 存储失败回调
        let resolve = (value) => {
            // ... 修改状态,存储 value 值
            // 依次执行存储的回调函数
            // 如果 resolve 的 value 是 Promise,则执行 .then 取到最终值再返回
            if (value instanceof Promise) {
                return value.then(resolve, reject)
            }
            this.onFullfilledCallbacks.forEach(fn => fn());
        }
        let reject = (reason) => {
            // ...
            this.onRejectedCallbacks.forEach(fn => fn());
        }
    }
    then(onFullfilled, onRejected) {
        // ...
        // 如果是异步,未立即修改 Promise 对象的状态,存储用户传入的成功、失败回调函数,
        if (this.status === PENDING) {
            this.onFullfilledCallbacks.push(() => {
                // aop 切面思想,预留处理其它问题
                onFullfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            })
        }
    }
}

到这里实现了存在异步时,等待返回最终状态再执行回调,使用下

new Promise((resolve) => {
    setTimeout(() => {
        resolve('ok')
    },2000)
}).then(res => console.log('2秒后', res)) // 等2秒后输出结果

加强 🔨🔨

Promise支持无限链式调用,每次.then又返回一个可以.thenPromise对象

function resolvePromise(promise2, x, resolve, reject) {
    // promise2 === x 会造成循环调用直接抛错
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise'))
    }
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, y => {
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    reject(r)
                })
            } else {
                // 普通对象或普通函数
                resolve(x)
            }
        } catch(e) {
            reject(e)
        }
    } else {
        // 普通值直接返回
        resolve(x)
    }
}

class Promise {
    constructor(props) {
        // ...
    }
    then(onFullfilled, onRejected) {
        // 当未传入 onFullfilled/onRejected 时,给默认函数
        onFullfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onFulfilled === 'function' ? onRejected: err => { throw err }
        // 每次执行 .then 返回一个新的 Promise 对象
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === RESOLVED) {
                // 添加定时器是因为当处理当前结果时还取不到 promise2
                setTimeout(() => {
                    // 捕获异步中出现的错误
                    try {
                        // 执行用户的成功回调,取到返回结果
                        let x = onFullfilled(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) {
                this.onFullfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFullfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2;
    }
}

实现了

  • promise链式调用
  • then返回Promise对象时取到最终数据的处理
  • 异常捕获处理

至此 Promise的实现全部完成 🎉,如果有帮助请点赞分享,感谢!

参考