promise函数自定义封装详解(三)

259 阅读10分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

promise函数自定义封装详解(三)

大家好,我们在上篇文章当中完成了异步任务的成功执行,promise构造函数优化,then方法返回值完善。那在这篇文章当中,我们还将持续的优化then方法里的一些代码逻辑和组成架构,除此之外,我们还需要在promise构造函数上添加resolve和reject的方法,因此这节课的主要任务如下

目标任务

  1. 完善then方法的内部代码
  2. 封装common函数,提高代码的可读性
  3. 在promise构造函数上挂载一个resolve和reject方法

完善then方法

思考

之前我们在script的then方法调用里执行相关函数时,基本都是console.log(...),简单的打印,而我们必须知道之前声明的res结果值和状态是受then方法调用时内部的方法执行的结果决定的。换句话说,then方法里的函数体中可能会有各种各样的代码,但总的来说有两种,一种是promise类型对象,一种是非promise类型对象(例如undefined,null,console.log等一些字符串),都是能够影响res结果的因素。那这又该怎么理解呢?

内置promise函数效果

我们利用JS原来内置的promise函数执行一些操作

          // 创建一个promise实例化对象
        let p = new Promise((resolve, reject) => {

            resolve('成功')

        })

        // 调用promise的then方法
        let res = p.then(value => {
            return new Promise((resolve, reject) => {
                reject('失败了哦')
            })
        }, reason=>{
              console.log(reason)
        })
        console.log(res);

来看res的打印结果:

image.png

可以看到这时候res的执行结果中状态为rejected,结果值也发生了改变,这就充分说明res的值是受then方法的返回值影响的。

自定义js文件函数效果

与此同时,再来看看我们引入创建的js文件之后的打印效果:

image.png

这时打印的res的值都为默认值,与原生promise的打印结果不一样,那我们该如何使其保持一致呢? 我们来分析为什么会出现这种情况,首先我们把then里形参为value的函数传到js文件中的then方法里时,此时onResolved就是这个函数,而这时候的实例p的状态为"fulfilled",所以我们紧接着我们执行if里的语句

image.png

此时这个onResolve函数语句已经执行,再强调一遍,后面括号里的参数并不一定参与执行。可以看到这个函数执行的结果为返回一个新的promise对象,而这个对象身上已经拥有了自己的状态值和结果值,因为内部我们设置了rejected

image.png

思路与代码

那此时回到js模块中刚才图中的位置,现在来想想,如果要让res的值被then方法的返回值影响,那我们是不是该对这个返回值的类型进行判断呢?答案是肯定的

因此我们可以设置一个变量来接收这个onResolve的执行结果,再对这个result的类型进行判断,如果它为非promise类型,则我们直接让它成功执行,就不再执行后续的代码。如果此时result的类型又为promise对象,那我们就要进行相同的操作,调用then方法,再次对这个对象进行处理,整个过程可以利用try...catch...来进行捕捉可能存在的状态异常,具体代码如下

状态为成功的代码

 return new Promise((resolve, reject) => {
        // 成功时的then方法的调用
        if (this.PromiseState === 'fulfilled') {
            try {
                // 声明result变量来接收函数执行结果
                let result = onResolved(this.PromiseResult)

                // 对result的结果进行判断
                if (result instanceof Promise) {
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(result)
                }

            } catch (e) {
                reject(e)
            }

        }

        // 失败时的then方法的调用
        if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult)
        }

        // 状态为pending时then方法的调用
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: onResolved,
                onRejected: onRejected
            })
        }
    })

此时我们再来打印res的值,如图

image.png

这样一来,我们就成功实现了与js内置promise函数相同的执行结果。

我们再来看下面这种情况

image.png

来看打印结果:

image.png

这时候我们遇到了跟上面一样的问题,那同样的,我们再次在js文件里对then方法里,对判断为‘rejected’的语句进行相同的操作

状态为失败的代码

   // 失败时的then方法的调用
        if (this.PromiseState === 'rejected') {
            try {
                // 声明result变量来接收函数执行结果
                let result = onRejected(this.PromiseResult)

                // 对result的结果进行判断
                if (result instanceof Promise) {
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(result)
                }

            } catch (e) {
                reject(e)
            }

        }

只不过这时候执行的函数为onRejected,来看看打印结果:

image.png

此时我们就完成了打印,取得了相应的成功结果。

状态为"pending"时的代码

同样的,我们可以把判断状态为'pending'时的代码也完善一下,这时候的函数我们可以进行替换,在我们新定义的函数内部利用try...catch来完善代码逻辑,具体代码如下:

  if (this.PromiseState === 'pending') {
            this.callbacks.push(
                {
                    onResolved: function () {
                        try {
                            //执行成功回调函数
                            let result = onResolved(self.PromiseResult);
                            //判断
                            if (result instanceof Promise) {
                                result.then(v => {
                                    resolve(v);
                                }, r => {
                                    reject(r);
                                })
                            } else {
                                resolve(result);
                            }
                        } catch (e) {
                            reject(e);
                        }
                    },
                    onRejected: function () {
                        try {
                            //执行成功回调函数
                            let result = onRejected(self.PromiseResult);
                            //判断
                            if (result instanceof Promise) {
                                result.then(v => {
                                    resolve(v);
                                }, r => {
                                    reject(r);
                                })
                            } else {
                                resolve(result);
                            }
                        } catch (e) {
                            reject(e);
                        }
                    }
                }
            )
        }

这里回顾复习一下,如果判断当前实例的状态为"pending"时,我们把onResolved函数和 onRejected函数整体打包成对象追加到callbacks数组里,当定时器被触发时,promise构造函数内部便会执行相应函数。

判断函数传参是否完整

此时我们再来进行如下测试,当script模块里只有一个箭头函数时,那这时候js文件中then方法只接受了一个函数作为参数,那么这时候打印结果是什么呢?请看如下代码:

        // 欢迎来到 我的世界-
        let p = new Promise((resolve, reject) => {

            reject('亲')

        })

        let res = p.then(value => {
            console.log(value);
        })
        console.log(res);

打印结果:

image.png

这时候可以看到PromiseResult的值报错,出现了这种情况是因为我们exector函数中是reject(...),在调用then时它应该执行第二个函数,但我们没有设置第二个函数,所以这时候有问题。

那相应的我们该怎么完善代码呢?思路很简单,当调用then函数时,先利用if语句判断传进来的参数是否完整,如果不完整再分类讨论,请看代码

        if (typeof onResolved !== 'function') {
            onResolved = value => value
        }


        // 若用户没有传入onRejected箭头函数
        if (typeof onRejected !== 'function') {
            onRejected = resaon => {
                throw resaon
            }
        }
  • 如果判断传进来的onResolved不是一个函数,那么我们直接让它返回其本身的值,并赋值给变量onResolved,参与接下来的运算。

  • 如果判断传进来的onRejected不是一个函数,那么我们直接定义一个函数,函数体内抛出值为它本身值的错误,在赋值给onRejected,参与接下来的运算

此时我们再来打印结果,如图

image.png

这时候我们就完成了res值的正确打印,这里大家可以先停一停,代码书写完成之后,自己在脑海里走一下刚才的逻辑和运算

封装函数

此时我们发现上面三种状态里的代码大致相同,那我们此时可以对这些函数进行封装,命名为common,所以当我们需要的时候,执行对应的状态里的common函数就行了,来看代码

代码实现

   // 返回一个promise对象
    return new Promise((resolve, reject) => {


        // 封装common函数
        function common(type) {
      
            try {
              //type为传进来的参数,实质为一个函数
              
                // 执行函数,并且把执行结果赋值给result变量
                let result = type(self.PromiseResult)

                if (result instanceof Promise) {
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(result)
                }

            } catch (e) {
                reject(e)
            }
        }

        // 成功时的then
        if (this.PromiseState === 'fulfilled') {
            setTimeout(() => {
            //调用common函数
                common(onResolved)
            })
        }

        // 失败时的then
        if (this.PromiseState === 'rejected') {
            setTimeout(() => {
              //调用common函数
                common(onRejected);
            })

        }


        // 异步执行的then
        if (this.PromiseState === 'pending') {
            self.callbacks.push(
                {
                    onResolved: function (data) {
                        common(onResolved)
                    },

                    onRejected: function (data) {
                        common(onRejected)
                    }
                }
            )
        }
    })

这样一来我们就完成了对common函数的封装,大大的提高了代码的可读性和维护性,并改善了代码的可移植性。

对promise函数挂载方法

如果我们此时直接调用promise的resolve方法,传一个参数进去,让p来接受整体的返回值,打印p又会有什么结果呢

image.png

打印结果:

image.png

可以看到控制报错说Promise.resolve不是一个函数,这是因为我们没有在promise构造函数上挂载相应函数,因此我们要在promise身上分别挂载resolve方法和reject方法

注意:这里注意区分函数体内部添加新函数和函数上挂载新方法不一样!

resolve函数

        // 给构造函数Promise挂载resolve函数
        Promise.resolve = function (data) {
            return new Promise((resolve, reject) => {
                if (data instanceof Promise) {
                    data.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(data)
                }
            })
        }

简单理解一下,挂载的resolve函数执行结果是一个新的promise对象,再来对传进来的data数据的类型,如果promise类型,那么再从在表达式里面进行判断,直到最终的结果为非promise类型。如果传进来的是一个非promise类型,则直接执行构promise类型里的resolve方法,对实例进行绑定结果值和状态值

reject函数

// 给构造函数Promise挂载reject函数
Promise.reject = function (data) {
    return new Promise((resolve, reject) => {
        reject(data)

    })
}

这里与上面的唯一不同点就是我们无需对reject里data值的类型进行判断,直接调用promise构造函数里的reject方法就可对实例进行绑定结果值和状态值

此时我们再来打印,如图

image.png

结果显示成功打印,说明我们对promise挂载的两个方法及编写无误

注意: 这里的resolve和reject方法均返回的是一个promise对象

本节总结

这节课我们实现了如下的预期目标

  1. 完善then方法的内部代码
  2. 封装common函数,提高代码的可读性
  3. 在promise构造函数上挂载一个resolve和reject方法

注意编写代码之前一定要先搞清出我们要实现的功能是什么,可能需要用到哪些方法,逻辑怎么样能够衔接起来,最后再来整理思路,编写代码。

在后面的文章当中,我们要对promise构造函数继续挂载catch方法all方法race方法。这节课先到这里,谢谢大家

html代码

  // 欢迎来到 我的世界
    // let p = Promise.resolve('132')


    // 基本格式
    let p = new Promise((resolve, reject) => {
        resolve()
        // reject()
    })

    let res = p.then(value => {
        //   ...
    }, reason => {
        // ...
    })

    console.log(p);

可参考本节内的代码复制模块

js文件代码

// 定义promise构造函数
function Promise(executor) {
    // 设置默认状态和默认值
    this.PromiseState = 'pending'
    this.PromiseResult = null

    // 设置一个空数组,异步时调用
    this.callbacks = []

    // this赋给self
    const self = this

    // 成功时的函数
    function resolve(data) {
        // 判断此时的p的状态是否已经改变
        if (self.PromiseState !== 'pending') return

        // 设置实例成功时的状态和结果值
        self.PromiseState = 'fulfilled'
        self.PromiseResult = data

        // 执行调用函数为resolve的异步任务
        self.callbacks.forEach(item => {
            item.onResolved(data)
        })
    }

    // 失败时的函数
    function reject(data) {
        // 判断此时的p的状态是否已经改变
        if (self.PromiseState !== 'pending') return


        // 设置实例成功时的状态和结果值
        self.PromiseState = 'rejected'
        self.PromiseResult = data

        // 执行调用函数为reject的异步任务
        self.callbacks.forEach(item => {
            item.onRejected(data)
        })
    }


    // 执行函数并捕捉可能存在的异常
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

// 在promise函数原型上挂载一个then方法
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this
    // 若用户没有传入onResolved箭头函数
    if (typeof onResolved !== 'function') {
        onResolved = value => value
    }


    // 若用户没有传入onRejected箭头函数
    if (typeof onRejected !== 'function') {
        onRejected = resaon => {
            throw resaon
        }
    }


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


        // 封装common函数
        function common(type) {
            try {
                // 执行函数,并且把执行结果赋值给result变量
                let result = type(self.PromiseResult)

                if (result instanceof Promise) {
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(result)
                }

            } catch (e) {
                reject(e)
            }
        }

        // 成功时的then
        if (this.PromiseState === 'fulfilled') {
            // setTimeout(() => {
            common(onResolved)
            // })
        }

        // 失败时的then
        if (this.PromiseState === 'rejected') {
            // setTimeout(() => {
            common(onRejected);
            // })

        }


        // 异步执行的then
        if (this.PromiseState === 'pending') {
            self.callbacks.push(
                {
                    onResolved: function (data) {
                        common(onResolved)
                    },

                    onRejected: function (data) {
                        common(onRejected)
                    }
                }
            )
        }
    })

}

// 给构造函数Promise挂载resolve函数
Promise.resolve = function (data) {
    return new Promise((resolve, reject) => {
        if (data instanceof Promise) {
            data.then(v => {
                resolve(v)
            }, r => {
                reject(r)
            })
        } else {
            resolve(data)
        }
    })
}


// 给构造函数Promise挂载reject函数
Promise.reject = function (data) {
    return new Promise((resolve, reject) => {
        reject(data)

    })
}