美文网首页
手写Promise

手写Promise

作者: 邓立_全栈UncleLi | 来源:发表于2021-06-17 18:01 被阅读0次

    今天来玩一下Promise,一步步分析。

    • 原版Promise效果
    let promise = new Promise((resolve, reject) => resolve("success"));
    
    promise.then(msg => console.log(msg));
    
    控制台调试结果
    • 自定义一个Pomise
    class MyPromise {
    
        constructor(func) {
            this.callbacks = []
    
            // 绑定this指向
            const resolve = this.resolve.bind(this)
            func(resolve)
        }
    
        resolve(e) {
            this.callbacks.forEach(cb => cb(e))
        }
    
        reject() {
            console.error('reject')
        }
    
        then(cb) {
            this.callbacks.push(cb)
        }
    }
    
    new MyPromise((resolve, _) => setTimeout(() => resolve('success'), 0)).then(e => console.log(e))
    
    • 执行结果


      执行结果
    • 此时看起来似乎实现了功能,但是当new MyPromise的时候把异步方法变成同步方法,此时then方法里无法打印出值,如:

    new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e))
    
    • 此时应该想办法把then方法里的函数放在resolve方法执行之前,所以采取浏览器事件触发线程,把事件添加到待处理JS任务队列的队尾,等待js引擎的处理,让resolve执行的时时候then方法的回调函数已经存在回调数组callbacks中,如setTimeout方法。这里就有人疑问了,不是刚去掉吗,怎么又加上哈哈。。。的确,但是加的位置不一样了。不过这里加的是微任务queueMicrotask,和setTimeout宏任务比起来微任务执行优先级更高,如:
    class MyPromise {
    
        constructor(func) {
            this.callbacks = []
    
            // 绑定this指向
            const resolve = this.resolve.bind(this)
    
            // 此处加上queueMicrotask微任务方法
            queueMicrotask(() => func(resolve))
        }
    
        resolve(e) {
            this.callbacks.forEach(cb => cb(e))
        }
    
        reject() {
            console.error('reject')
        }
    
        then(cb) {
            this.callbacks.push(cb)
        }
    }
    
    new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e))
    
    • 熟悉的结果回来了。要是执行中途出现异常,那就捕获异常并返回reject,如:
    try {
          queueMicrotask(() => func(resolve))
    } catch (error) {
          this.reject()
    }
    
    • 接着有人问,这里只有一个then方法,那么多个then方法怎么办呢?好办,链式调用,在then方法中return this,如:
    then(cb) {
        this.callbacks.push(cb)
    
        // 链式调用
        return this
    }
    
    • 输出代码
    new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e)).then(e => console.log(e))
    
    • 执行结果


      执行结果
    • 这样看着可以执行多个then方法了,可是还有问题,细心的小伙伴发现了then方法执行后的结果每次都是一样的,并没有随着上次执行的变化而变化,如:

      执行结果
    • 两次then方法中取到的数据都是1。所以要把then方法返回的值保存一下

    class MyPromise {
    
        constructor(func) {
            // 初始化值
            this.value = null;
    
            this.callbacks = []
    
            // 绑定this指向
            const resolve = this.resolve.bind(this)
            queueMicrotask(() => func(resolve))
        }
    
        resolve(e) {
            this.value = e
            this.callbacks.forEach(cb => {
                let newValue = cb(this.value)
    
                // 把then方法返回的值存起来
                this.value = newValue
            })
        }
    
        reject() {
            console.error('reject')
        }
    
        then(cb) {
            this.callbacks.push(cb)
            return this
        }
    }
    
    • 执行结果


      image.png
    • 然后还有小伙伴说,当最后一次then方法是个耗时好操作怎么办,只会输出一个数值,如:

      执行结果
    • 这里遵循Promise源码的规定,在Promise源码中是有状态这一说的,有种状态,pending - 进行中fulfilled - 成功rejected - 失败。当主体执行完后,把状态变更一下,后续再执行直接把后续加上的then方式执行即可,所以实现一下,如:

    // 状态对象
    const stateObj = {
        PENDING: 'pending',
        FULFILLED: 'fulfilled',
        REJECTED: 'rejected'
    }
    
    class MyPromise {
    
        constructor(func) {
    
            // 初始化状态
            this.state = stateObj.PENDING
    
            // 初始化值
            this.value = null;
    
            this.callbacks = []
    
            // 绑定this指向
            const resolve = this.resolve.bind(this)
    
            try {
                queueMicrotask(() => func(resolve))
            } catch (error) {
                this.reject()
            }
        }
    
        resolve(e) {
            this.value = e
    
            // 改变状态
            this.state = stateObj.FULFILLED
            this.callbacks.forEach(cb => {
                let newValue = cb(this.value)
    
                // 把then方法返回的值存起来
                this.value = newValue
            })
        }
    
        reject() {
            console.error(stateObj.REJECTED)
        }
    
        then(cb) {
            if (this.state === stateObj.PENDING) {
                this.callbacks.push(cb)
                return this
            }
    
            try {
                // 后续调用直接执行传进来的方法
                cb(this.value)
            } catch (error) {
                this.reject()
            }
            return this
        }
    }
    
    const mp = new MyPromise((resolve, _) => resolve(2)).then(e => {
        console.log(e)
        return e * 2
    })
    
    setTimeout(() => {
        mp.then(e => {
            console.log(e)
            return e * 2
        })
    }, 3000)
    
    • 执行结果又回来了


      执行结果

    好咧,今天就玩到这里,后续继续优化

    相关文章

      网友评论

          本文标题:手写Promise

          本文链接:https://www.haomeiwen.com/subject/yqaqyltx.html