美文网首页
Promise实现

Promise实现

作者: Egde | 来源:发表于2019-04-05 17:07 被阅读0次

实现Promise根据Promise规范
规范很短,所以每句都很重要

Promise特点

  1. 状态改变后不可再改变(状态凝固)
    pending,fullfilled,rejected
  2. then方法(此方法就是最重要的部分)
    可以链式调用
    then方法返回一个新的promise实例
    promise2 = promise.then(f1, f2)
    当promise的状态确定的时候会执行then中的f1或者f2。
    在f1/f2执行之前then就已经返回了一个Promise实例(promise2)
    promise2的状态由f1/f2的返回值确定,以此类推就是链式调用
  3. promise创建时传入的函数会立即执行
    在考虑某些函数执行时同步还是异步,考虑四种情况,分别会有什么问题
    假设new一个Promise时传入的函数为funA, then中的函数为funCD
    1 funA是同步,funCD为同步
    2 funA是同步,funCD为异步 // 为什么最后只能用这种方式
    3 funA是异步,funCD为同步
    4 funA是异步,funCD为异步

问题:

  1. 状态是怎么改变的
    在执行resolve,reject函数时会改变状态。看resolve,reject的函数声明
  2. new Promise时传入的函数为什么要立即执行,then里传的两个函数为什么要异步执行?
    resolve,reject函数是用户决定什么时候执行的,同步、异步执行都是用户的行为
    这里可以好好分析下,信息量很大
    状态确定后挨个调用数组中的方法
    状态是在resolve,reject函数执行时改变的

代码

Promise构造函数
function Promise(executor) {
    var self = this
    /**
    * 为什么要创建数组?
    * new出第一个实例promise后,可以调用多次then
    * 而此时promise的状态可能还不确定,所以来个数组保存下then中的函数,等到状态确定后再执行then中的函数。
    * 保存then中的回调函数,注意查看什么时候push进去的
    */
    self.resolvedCallbacks = []
    self.rejectedCallbacks = []

    // 状态
    self.status = 'pending'

    /*
    * resolve中应该包含一个对象,因为每次创建一个实例
    * 彼此间都不会有影响(这难道不是单例模式么?还是工厂函数?)
    * 状态[确定]后挨个调用数组中的方法,必须被当做函数调用。
    * 即调用时函数体内的this应该是undefined/window
    * --------------------------------------------------
    * 成功传值,失败传原因
    * --------------------------------------------------
    * resolve和reject是什么时候被执行的呢?
    * 这两个函数是在executor体内被执行的
    * --------------------------------------------------
    * 对于在resolve,reject两个函数中
    * 异步执行resolvedCallbacks,rejectedCallbacks中函数问题
    */
    function resolve(value) {
        // 测试用例给我们传了一个我自己定义的Promise实例时
        if (value instanceof Promise) {
            value.then(resolve, reject)
            return
        }

        // 这里为什么也要异步
        // 因为数组resolvedCallbacks,rejectedCallbacks不能跟resolve同步执行。为什么?
        setTimeout(function () {
            /**
            * 这里是为了确保状态不可改变,即resolve/reject只执行一次
            */
            if (self.status === 'pending') {
                self.status = 'resolved'
                // 保存传入的参数,promise成功时的值
                self.data = value
                var f
                // 性能优先,使用原始for循环
                for (var i = 0; i < self.resolvedCallbacks.length; i++) {
                    /**
                    * 为什么分开写?
                    * 确保是函数的调用,即被调用时函数体内的this为undefined/window
                    * 标准里都有说明
                    */
                    f = self.resolvedCallbacks[i]
                    f(value)
                }
            }
        })
    }

    function reject(reason) {
        // 为什么是异步
        setTimeout(function () {
            if (self.status === 'pending') {
                self.status = 'rejected'
                self.data = reason
                var f
                for (var i = 0; i < self.rejectedCallbacks.length; i++) {
                    f = self.rejectedCallbacks[i]
                    f(reason)
                }
            }
        })
    }
    try {
        // promise创建时传入的函数会立即执行,而传入的函数不知道有没有问题.所以要try下
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
then方法

then方法中实现根据状态调用不同的函数,
并且在调用回调前返回一个新的promise且此promise的状态由回调函数确定
pending -> fulfilled : 调用onResolved
pending -> rejected : 调用onRejected
onRejected\onResolved必须是函数,否则忽略它

Promise.prototype.then = function (onResolved, onRejected) {
    var self = this
    if (typeof onResolved !== 'function') {
        /*
        * 为什么要这样?
        * 例如这种情况:p2 = p1.then(null, f2),没有传onResolved函数
        *  p1成功了,应该调用onResolved函数。
        *  正常情况下p2的状态由onResolved确定,而onResolved没有东西。
        *  此时p2的状态应该由p1决定。
        *  怎么拿到p1的状态?在then中成功时调用的函数将p1的状态返回就拿到p1的状态!
        *  在没有返回函数情况下,将onResolved用一个函数代替
        * ---------------------------------------------------------------
        *  为什么可以这么写,我没有吃透
        *
        *  这个函数被执行的时候value就有值了,状态改变onResolved(self.data)执行
        *  在这种情况下value的值查查self.data
        *  self.data是在构造函数里的resolve(value)里被赋值的。所以这里非常的绕,还需要再来理解消化
        *
        */
        onResolved = function (value) { return value }
    }
    if (typeof onRejected !== 'function') {
        /*
        * 为什么要这样?
        * 例如这种情况:p2 = p1.then(null, f2),没有传onResolved函数
        *  p1失败,p2也应该失败。
        *  这里throw,后面的try可以catch到。catch到就会reject(e)
        *  将p1的失败reason抛出来,p2也就失败了
        *  具体的过程跟上面的函数是一样的
        */
        onRejected = function (reason) { throw reason }
    }
    /*
    * 实例在三种状态下都可以调用then方法
    * 判断状态的三种情况。为了方便
    * 在三种情况下
    * 我要实现的是
    */
    var promise2
    if (self.status === 'resolved') {
        /*
        * 返回一个新的promise2,在promise2中调用onResolved且传入promise成功时传入的值
        * 而promise成功时传入的值到底是什么,我们是不知道的。
        * 这是promise中最复杂的!!!
        * ----------------------------------------------------------------------
        * onResolved返回值x决定了promise2的状态
        *
        */
        promise2 = new Promise(function (resolve, reject) {
            // 为什么这里要异步?TODO
            setTimeout(function () {
                try {
                    /*
                    * 这个data是第一个promise中传入的函数中的resolve携带的参数!!!!
                    * onResolved是在then里的,
                    * 如果onResolved返回的又是Promise实例x呢,
                    * --------------------------------------------------------------
                    * 这次的promise的状态是由返回的新的Promise实例决定
                    * 而新的实例的状态什么时候确定呢?我们不用管,只需要在其后挂个then。
                    * then中函数运行的时候就是状态确定的时候
                    * --------------------------------------------------------------
                    * 直接resolve是不行的,需要在返回的实例x中的then里resolve值
                    */
                    var x = onResolved(self.data)
                    // 非兼容
                    // if (x instanceof Promise) {
                    //  // 非简化。可以想想了,又是递归?
                    //  // x.then(function(value) {
                    //  //  resolve(value)
                    //  // }, function(reason) {
                    //  //  reject(reason)
                    //  // })
                    //  // 简化
                    /*
                    * 这种简化让人头疼,resolve,reject是需要传参的。问题来了,两个函数的参数是怎么传进去的
                    * 这里的resolve,reject其实就是onResolved和onRejected。这种情况又是递归了
                    */
                    //  x.then(resolve, reject)
                    // } else {
                    //  resolve(x)
                    // }

                    // 兼容下
                    RESOLVE_PROMISE(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            // 为什么这里要异步?TODO
            setTimeout(function () {
                try {
                    var x = onRejected(self.data)
                    // 非兼容,如果x是Bluebird,Q里的promise呢?if就不会执行了
                    // if (x instanceof Promise) {
                    //  x.then(resolve, reject)
                    // } else {
                    //  // 为什么是resolve?
                    //  resolve(x)
                    // }
                    // 兼容
                    RESOLVE_PROMISE(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
    if (self.status === 'pending') {
        /*
        * promise1还是不确定状态,需要等到promise1确地了状态才能确定promise2应该怎么走
        * 查看构造函数里resolve函数的注释,找找灵感。
        * -------------------------------------------------------
        * 为什么在这就不需要像上面的‘resolved’,‘rejected’里异步呢?
        *    因为这里肯定是在将来才执行的
        */
        promise2 = new Promise(function (resolve, reject) {

            self.resolvedCallbacks.push(function (value) {
                try {
                    var x = onResolved(self.data)
                    // 非兼容
                    // if (x instanceof Promise) {
                    //  x.then(resolve, reject)
                    // } else {
                    //  resolve(x)
                    // }

                    // 兼容
                    RESOLVE_PROMISE(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })

            self.rejectedCallbacks.push(function (reason) {
                try {
                    var x = onRejected(self.data)
                    // 非兼容
                    // if (x instanceof Promise) {
                    //  x.then(resolve, reject)
                    // } else {
                    //  resolve(x)
                    // }

                    // 兼容
                    RESOLVE_PROMISE(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
    return promise2
}
兼容函数

上面就基本已经实现了Promise,但是没有达到规范的要求。
这个函数是多种实现中的一种
这个函数的作用是使其他的Promise可以互相兼容------兼容

/**
* 各参数说明
*    promise:
*    x:
*    resolve:
*    reject:
*/
function RESOLVE_PROMISE(promise, x, resolve, reject) {
    if (promise === x) {
        // 一个promise实例resolve了它自己
        reject(new TypeError('Chaining cycle is deteced for promise'))
        return
    }

    // 如果x是自己的Promise的实现
    if (x instanceof Promise) {
        if (x.status === 'pending') {
            x.then(function (v) {
                // god,又是递归
                RESOLVE_PROMISE(promise, v, resolve, reject)
            }, reject)
        } else {
            x.then(resolve, reject)
        }
        return
    }

    // 只有对象和函数这两种数据类型可以保存属性,然而typeof null也是'object'
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            /*
            * 为什么要把then单独拿出来?避免副作用
            *  因为我们是在读别人给的属性,但是我们不知道该属性具体是什么。也可能报错
            *  如果该属性是getter,则每次调用就会返回不同的值。
            * 所以只调用一次
            * ---------------------------------------------------------------------
            * a,b,c三者只能调用一个,为什么?TODO
            *  避免原型链上有then函数(thenable)但却不是Promise的实现,但是为什么这样就可以避免,我并没有吃透
            * ---------------------------------------------------------------------
            * if语句里有两个可以可疑的函数,有函数名resolvePromise,rejectPromise。
            * 既然是当参数传,我觉得不需要函数名应该也一样吧
            */
            var then = x.then
            var called = false // 记录执行状态
            if (typeof then === 'function') {
                then.call(x, function resolvePromise(y) {
                    if (called) {
                        return
                    }
                    called = true
                    // 产生了递归
                    RESOLVE_PROMISE(promise, y, resolve, reject)//--- a
                }, function rejectPromise(r) {
                    if (called) {
                        return
                    }
                    called = true
                    reject(r) //------------------------------------- b
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) {
                return
            }
            called = true
            reject(e) //----------------------------------------- c
        }
    } else {
        resolve(x)
    }
}

相关文章

网友评论

      本文标题:Promise实现

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