Promise函数自定义封装详解(一)

Promise的自定义封装(一)

大家好,今天我们来学习一下手写promise函数的源码,完成对promise函数功能和方法的封装。

首先,我们需要明白什么是promise函数,以及它的作用,内置的API等。

Promise函数的基本理解

简单来说,promise函数是为了解决异步回调时的一些问题,更准确来说是为了解决我们在开发中,经常会面临的“回调地狱”所给开发者带来的困扰。promise可以被理解成一个方案,来有助于我们快速规避这些麻烦,从而提高我们的开发效率。

我们知道一个promise函数的状态必然处于以下几种状态中的一个:

  • 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled) :意味着操作成功完成。
  • 已拒绝(rejected) :意味着操作失败。

promise内置的几种方法

既然我们知道了promise的作用之后,我们就来谈谈promise函数所内置的几个API,也就是方法,这些方法在我们往后的学习和工作当中会被反复的使用,因此要熟悉这些API能实现的功能变的尤为重要:

  • resolve:promise构造函数上挂载或在内部定义的方法,表示一个“成功”时的状态,且此状态一旦生成就不可逆,此过程也称为“敲定”了这个状态。
  • reject:promise构造函数上挂载或在内部定义的方法,表示一个“失败”时的状态,且此状态一旦生成就不可逆,此过程也称为“敲定”了这个状态。
  • then:promise.then方法返回的结果也是一个promise函数,准确来说是一个对象,这个对象中有then方法返回的promise对象及其所处的状态和结果值等,因此这个新对象也可以使用promise所内置的方法。
  • catch:catch在promise中经常用来快速捕捉异常,例如捕捉throw抛错时而导致的非正常状态,在自定义封装中我们用try …catch来搭配使用,catch返回值是一个失败的promise对象。
  • all: promise中all方法的参数接受一个数组,这个数组的成员要满足都为promise对象。如果所有的promise对象状态都为“fulfilled”,也就是成功,all方法返回一个状态也为成功的promise对象,此对象内部的结果值为此成功的数组。若有一个promise成员的状态为失败,那么all方法就会返回一个失败的promise对象,其状态为”rejected”,其内部结果值为这个数组中失败的promise函数的结果值。
  • race: promise中race方法的参数接受一个数组,这个数组的成员要满足都为promise对象,race方法返回一个promise对象,这个对象的状态由数组中第一个状态改变的promise函数决定,且两个对象的状态保持一致,且其结果值为数组中第一个状态改变的promise函数的结果值。

Promise函数自定义封装代码实现

1.html文件和js文件的基本配置

第一步,我们要创建一个html页面和一个js文件,并将这个js文件引入这个html页面中。此外我们需要在js文中定义一个promise构造函数,来实现对js原来内置promise函数的覆盖,如图

Promise函数自定义封装详解(一)

2.创建一个promise实例对象

第二步,我们要在html里的script模块中创建一个promise的实例化对象,并在此对象的参数中写入执行器函数(exector),并声明一个变量p来接收这个promise函数整体的返回结果,除此之外,我们可以在这个函数体内部调用一个resolve方法,供后期使用。如图:

Promise函数自定义封装详解(一)

此时我们打印p的结果,如图显示

Promise函数自定义封装详解(一)

这说明我们创建promise函数实例化成功,p内部的constructor函数成功指向定义的promise函数。实参executor(箭头函数)成功传入js文件中的promise的形参,只不过promise函数体内没有任何代码,所以没有其他执行结果。

3.promise构造函数内部基本配置

第三步,我们要在这个定义的promise函数中添加这么几个部分:

1.在promise构造函数内部设置默认的状态值pending和结果值null

2.把构造函数内部的this指向赋值给变量self,传入到下面的函数

3.设置一个空数组callbacks,供后期执行异步操作时使用

4.设置一个resolve函数,在其内部设置实例对象p成功时的状态值“fulfilled”和结果值data

5.设置一个reject函数,在其内部设置实例对象p失败时的状态值“rejected”和结果值data

6.利用try…catch来执行传来的exector函数并捕捉可能存在的异常

具体代码如下:

function Promise(exector) {
    // 设置默认状态和默认值
    this.PromiseState = 'pending'
    this.PromiseResult = null

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

    // this赋给self
    const self = this

    // 成功时的函数
    function resolve(data) {

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

    }

    // 失败时的函数
    function reject(data) {

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

    }

    // 执行函数
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

4.打印实例p的三种状态所对应的对象

这样一来,我们再回到html页面中的script模块,打印p的结果,图如下:

Promise函数自定义封装详解(一)

可以看到,实例对象p内部状态值和结果值都对应了构造函数promise中所设置的数据。此时我们调用reject方法并打印实例p:

let p = new Promise((resolve, reject) => {
            reject('失败')
        })

        console.log(p);

Promise函数自定义封装详解(一)

我们调用在执行器函数内部不传入任何代码,打印实例p:

    let p = new Promise((resolve, reject) => {

        })
      console.log(p);

Promise函数自定义封装详解(一)

思考:为什么这里打印的值为null和pending呢?

5.定义promise函数的then方法

第四步,下面我们来调用promise的then方法,传入参数搭配原生promise函数then方法固定的格式,来看执行的结果

       let p = new Promise((resolve, reject) => {
            resolve('成功')
        })

        p.then(value => {
            console.log(value);
        }, reason => {

        })

打印结果:

Promise函数自定义封装详解(一)

注意:这里我们要实现的效果要与js内置promise函数所实现的效果一致,执行器函数内部是resolve(…)那我们就执行下面then括号中里的第一个函数。如果执行器内部是reject(…)那我相对应的就要执行then括号中而的第二个函数

出现这种情况是因为我们没有在js文件中定义then方法,所以我们可以在promise构造函数的原型上挂载一个then方法,供promise实例化对象使用。因此回到js文件中,我们要完成关于then方法的一些实现操作,我们要定义一个then函数并在这个then函数中进行如下步骤:

// 在promise函数原型上挂载一个then方法
Promise.prototype.then = function (onResolved, onRejected) {
    // 成功时的then方法调用
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult)
    }

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

     // 状态为pending时then方法的调用
    if (this.PromiseState === 'pending') {
        // ...
    }
}

如此一来我们就完成then方法的基本定义和内部代码编写,注意这里有几个点需要理解:

  • then方法中的this指向的是实例对象
  • 要对实例对象身上的状态值进行判断,再执行相应的代码
  • then()方法中的两个参数onResolved,onResolved分别对应script模块中then里的的两个箭头函数

现在我们来执行script代码,效果如下:

Promise函数自定义封装详解(一)

这样我们就成功的执行了then()里的第一个箭头函数value,执行结果为”成功”,与executor执行器函数内部方法的resolve里的结果保持了一致

本文总结

这节课我们完成了关于promise实例的声明和promise构造函数内部的代码的最基本实现,以及then方法内部的最基本逻辑结构的连接。后续的文章当中我们要完善很多代码并添加其他几种promise的方法,今天就先讲到这里,谢谢大家!

本节代码

html中script模块代码

  <script>
        // 创建一个promise实例化对象
        let p = new Promise((resolve, reject) => {
            // 调用resolve函数,并传入参数"成功"
            resolve('成功')
            // reject('失败')

        })

        // 调用promise的then方法
        p.then(value => {
            // 执行p状态成功时的回调
            console.log(value);
        }, reason => {
            // 执行p状态失败时的回调
            console.log(reason);
        })

    </script>

js文件中代码

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

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

    // this赋给self
    const self = this

    // 成功时的函数
    function resolve(data) {


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

    }

    // 失败时的函数
    function reject(data) {


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


    }


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

// 在promise函数原型上挂载一个then方法
Promise.prototype.then = function (onResolved, onRejected) {
    // 成功时的then方法的调用
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult)
    }

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

    // 状态为pending时then方法的调用
    if (this.PromiseState === 'pending') {
        // ...
    }

}