手写Promise和他的then方法

72 阅读2分钟

今天又复习了一下手写Promise和他的then方法,在手写的时候,对promise的原理和特性有了更深的了解,我就直接开写吧。

// 手写promise

// 首先定义promise的三个状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class myPromise {

    // 定义私有化变量(前面加上#的变量),为状态,返回结果和多次调用then时存储数据的数组
    #state = PENDING
    #result = undefined
    #handlers = []

    constructor(fn) {
        // 成功时返回resolve函数,同时改变promise状态,传入成功时的data
        const resolve = (data) => {
            this.#changeState(FULFILLED, data)
            
        // 失败时返回resolve函数,同时改变promise状态,传入失败时的reason
        }
        const reject = (reason) => {
            this.#changeState(REJECTED, reason)
        }
        
        // 调用fn函数时,使用try catch捕获错误,错误结果err传入reject
        try {
            fn(resolve, reject)
        }
        catch (err) {
            reject(err)
        }
    }

    // 捕获状态改变的方法
    #changeState(state, result) {
        // 如果是'pending',状态还没有改变
        if (this.#state !== PENDING) return
        // 状态改变了就传入改变的状态,并传入值
        this.#state = state
        this.#result = result
        //调用run函数
        this.#run()
    }

    // 把任务放进微任务队列(难点,选看)
    #mircoTask(func) {
        // node环境下的事件循环
        if (typeof process === 'object' && typeof process.nextTick === 'function') {
            process.nextTick(func)
        }
        // 浏览器环境下的事件循环
        else if (typeof MutationObserver === 'function') {
            const ob = new MutationObserver(func)
            const textNode = document.createTextNode('1')
            ob.observe(textNode, {
                characterData: true
            })
            textNode.data = '2'
        }
        // 如果都不是,就使用setTimeout把任务放进微任务队列
        else {
            setTimeout(func, 0)
        }
    }

    // 怕断传入的值是否是promise对象,promise对象只要符合:
    // 1.是对象或者函数类型
    // 2.有then方法
    // 就可以断定这个值为promise对象
    #isPromiseLike(value) {
        if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
            return typeof value.then === 'function'
        }
        return false
    }
    
    // 把任务放进微任务队列
    #runOne(callback, resolve, reject) {
        this.#mircoTask(() => {
            // 如果then里的值不是函数类型
            if (typeof callback !== 'function') {
                // 就判断状态是成功还是失败
                const settled = this.#state === FULFILLED ? resolve : reject
                settled(this.#result)
                return
            } else {
                // 如果是函数类型,就用try,cathch捕获异常
                try {
                    const data = callback(this.#result)
                    // 如果是promise对象,就接着调用then方法
                    if (this.#isPromiseLike(data)) {
                        data.then(resolve, reject)
                    } else {
                        // 否则就直接把data传入resolve
                        resolve(data)
                    }
                }
                // 错误直接传递err给reject
                catch (err) {
                    reject(err)
                }
            }
        })
    }

    // 通过while循环来链式调用then方法
    #run() {
        if (this.#state === PENDING) return
        while (this.#handlers.length) {
            // 解构出handlers中的这四个值
            const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift()
            // 来在微任务队列里(funOne函数)运行then方法里的函数或者值
            if (this.#state === FULFILLED) {
                this.#runOne(onFulfilled, resolve, reject)
            } else {
                this.#runOne(onRejected, resolve, reject)
            }
        }
    }

    // promise的then方法
    then(onFulfilled, onRejected) {
        // 返回一个新的promise对象
        return new myPromise((resolve, reject) => {
            // 把这几个都传入handlers数组
            this.#handlers.push({
                onFulfilled,
                onRejected,
                resolve,
                reject
            })
            // 运行run函数
            this.#run()
        })
    }
}