手写Promise源码

#### 手写Promise源码

// promise有三种状态 pending 等待 fulfilled 成功 rejected 失败

// 且状态改变只能从pending -> fulfilled(从等待到成功) 或者 pending -> rejected(从等待到失败) 且状态改变后不可更改

// 声明三种状态常量

const PENDING = 'pending'

const FULFILLED = 'fulfilled'

const REJECTED = 'rejected'

// 创建Promise类

class MyProise {

    // Promise 对象需要传入一个执行器函数 该执行器函数会立即执行 并且该执行器函数有两个参数成功回调 失败回调

    // 并且两个参数resolve和reject是来更改函数状态的

    // resolve -> fulfilled(从等待到成功)

    // reject -> rejected(从等待到失败)

    constructor(executor) {

        // 使用trycatch是为了捕获执行器函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回

        try {

            // 执行执行器函数 参数为更新函数状态的两个函数

            executor(this.resolve, this.reject)

        } catch (error) {

            // 捕获执行器函数异常 将Promise类的函数状态更改为rejected

            this.reject(error)

        }

    }

    //记录函数状态并初始化函数状态为pending

    status = PENDING

    // 记录函数成功后的返回值 作为成功返回值给then方法成功回调

    value = undefined

    // 记录函数失败后的返回值 作为失败原因给then方法失败回调

    reason = undefined

    // 收集函数状态为成功的回调函数

    successCallback = []

    // 收集函数状态为失败的回调函数

    failCallback = []

    // 执行器成功函数

    resolve = value => {

        // 函数状态更改后不可改变 若函数状态不为等待 拒绝向下执行

        if (this.status !== PENDING) return

        // 将函数状态更改为成功

        this.status = FULFILLED

        // 成功后的返回值

        this.value = value

        // Promise函数具有thenable接口且可以多次调用 导致可能会有多个回调函数执行

        // 使用while循环是因为数组变化导致循环体不成立时自动停止

        while (this.successCallback.length) this.successCallback.shift()()

    }

    // 执行器失败函数

    reject = reason => {

        // 函数状态更改后不可改变 若函数状态不为等待 拒绝向下执行

        if (this.status !== PENDING) return

        // 将函数状态更改为失败

        this.status = REJECTED

        // 失败后的返回值

        this.reason = reason

        // Promise函数具有thenable接口且可以多次调用 导致可能会有多个回调函数执行

        // 使用while循环是因为数组变化导致循环体不成立时自动停止

        while (this.failCallback.length) this.failCallback.shift()()

    }

    // 类的thenable接口

    then(successCallback, failCallback) {

        // then方法具有thenable接口 可以链式调用 需要返回一个promise对象

        // 创建Promise对象

        let promise = new MyPromise((resolve, reject) => {

            // then函数的状态取决于回调函数运行状态 现在判断状态

            if (this.status === FULFILLED) {

                // 状态为成功

                // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕

                // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值

                setTimeout(() => {

                    // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回

                    try {

                        let result = successCallback(this.value)

                        // result的值可能存在两种情况普通值和promise对象

                        // 如果为普通值直接调用resolve返回

                        // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject

                        // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错

                        // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数

                        // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject

                        resolvePromise(promise, result, resolve, reject)

                    } catch (error) {

                        // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回

                        reject(error)

                    }

                }, 0)

            } else if (this.status === REJECTED) {

                // 状态为失败

                // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕

                // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值

                setTimeout(() => {

                    // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回

                    try {

                        let result = failCallback(this.value)

                        // result的值可能存在两种情况普通值和promise对象

                        // 如果为普通值直接调用resolve返回

                        // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject

                        // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错

                        // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数

                        // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject

                        resolvePromise(promise, result, resolve, reject)

                    } catch (error) {

                        // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回

                        reject(error)

                    }

                }, 0)

            } else {

                // 状态为等待

                // 此时需要等待then方法执行完毕才能直到函数状态

                // 故将成功回调和失败回调存储起来 待状态更改之后在调用

                this.successCallback.push(() => {

                    // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕

                    // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值

                    setTimeout(() => {

                        // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回

                        try {

                            let result = successCallback(this.value)

                            // result的值可能存在两种情况普通值和promise对象

                            // 如果为普通值直接调用resolve返回

                            // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject

                            // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错

                            // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数

                            // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject

                            resolvePromise(promise, result, resolve, reject)

                        } catch (error) {

                            // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回

                            reject(error)

                        }

                    }, 0)

                })

                this.failCallback.push(() => {

                    // 在此逻辑中我们会用到变量promise 为确定变量promise有值 必须确定new MyPromise执行完毕

                    // 故我们使用定时器setTimeout 不是为了延迟而是为了异步执行以确定变量promise有值

                    setTimeout(() => {

                        // 使用trycatch是为了捕获函数中的运行异常 如出现异常则执行catch逻辑 将Promise类的函数状态更改为rejected并将失败原因返回

                        try {

                            let result = failCallback(this.value)

                            // result的值可能存在两种情况普通值和promise对象

                            // 如果为普通值直接调用resolve返回

                            // 如果为promise对象则根据promise的执行结果决定调用resolve还是reject

                            // 并且then方法返回的promise对象不能是当前then方法返回的对象, 否则会产生Promise对象的循环调用, JS会报错

                            // 此逻辑我们用一个函数来实现 因此我们需要传入四个参数

                            // 1. then方法返回值promise 2. then方法成功回调的返回值 3. 更改状态为成功resolve 4. 更改状态为失败reject

                            resolvePromise(promise, result, resolve, reject)

                        } catch (error) {

                            // 捕获函数异常 将Promise类的函数状态更改为rejected并将失败原因返回

                            reject(error)

                        }

                    }, 0)

                })

            }

        })

        // 返回then函数的promise对象

        return promise

    }

    // 类的finally方法 参数为回调函数

    // finally方法的内容都会被执行一次

    // finally方法之后可以链式调用then方法

    finally(callback) {

        // 在finally 内部可以通过then方法拿到 当前promise的状态

        // 并且then方法返回promise对象 可以链式调用then方法

        return this.then(value => {

            // 如果finally的回调函数是promise 需等待回调函数执行完成在将结果返回给之后的链式调用

            return MyProise.resolve(callback()).then(() => value)

        }, reason => {

            // 如果finally的回调函数是promise 需等待回调函数执行完成在将结果返回给之后的链式调用

            return MyProise.resolve(callback()).then(() => {throw reason})

        })

    }

    // 捕获异常

    catch(failCallback){

        // 使用then方法去注册失败回调 并置空成功回调

        return this.then(undefined, failCallback)

    }

    // promise.all()方法 参数为数组 返回结果为数组内的元素顺序(且为promise对象), 不论有无延迟

    // 解决异步并发问题

    static all(array){

        // 声明结果数组 作为all方法的返回值

        let result = []

        // all方法中有可能有promise的异步操作, 为保证参数数组中每一项都有返回值 需对返回结果修改时进行标记(累加处理)

        let index = 0

        return new MyProise((resolve, reject) => {

            // 将结果放入结果数组

            function addData(key, value){

                result[key] = value

                // 累加处理

                index++

                // 当所有结果都有返回值时 执行all方法的成功回调

                if(index === array.length){

                    resolve(result)

                }

            }

            // 遍历当前数组 并且判断循环体是普通值还是promise对象

            for(let i = 0; i < array.length; i++){

                // 劫持循环体

                let current = array[i]

                if(current instanceof MyProise){

                    // 循环体为promise对象 根据其运行结果调用resolve和reject更改状态

                    current.then(value => addData(i, value), reason => reject(reason))

                }else {

                    // 循环体为普通值

                    addData(i, current)

                }

            }

        })

    }

    // Promise.resolve()方法返回一个promise对象

    static resolve (value){

        // 如果value是promise对象直接返回

        if(value instanceof MyProise) return value

        // 如果value是普通值 返回一个promise对象 并且将value当作成功返回值返回

        return new MyProise(resolve => resolve(value))

    }

}

function resolvePromise(promise, result, resolve, reject) {

    // 如果then方法返回值promise 和 then方法成功回调的返回值 相同

    if (promise === result) {

        // 抛出错误 promise对象循环调用

        return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))

    }

    // 如果then方法成功回调的返回值 是promise对象

    if (result instanceof MyProise) {

        // 调用promise对象的then方法 根据执行结果决定调用resolve还是reject

        result.then(resolve, reject)

    } else {

        // 如果then方法成功回调的返回值 是普通值

        resolve(result)

    }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容