手写promise 浅析简单的promise源码的实现

 

Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.它是用于解决回调地狱的尴尬与丑陋的神器。

这里附上MDN地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

可以看下具体的用法。但是今天我们试着手写一个简单(简陋)的promise,来进一步认识她是如何处理异步操作的。

就像汽车行业的逆向研发一样,我们也是根据 promise使用的API,来倒推出promise源码,这样会比较好理解。

 

1、promise的用法

想要写源码,我们其实根据其API倒退我们准备写的源码。毕竟我们现在是在逆向开发。所以看看我们用常用的promise用法。先看一下最常用的以下两种场景:

//场景一:直接调用 -> 这就意味着在写源码的时候 Promise方法上本身绑定的就有 resolve 或者 reject的属性

Promise.resolve(1000); // -> 成功的值
Promise.reject('some error'); // -> 失败的值

//场景二:实例化 -> 这就意味着原型方法上要有个 then的方法
new Promise((resolve, reject) => {
    // 这里来个异步的代码
    settimeout(() => {
        resolve(1000);
    },1000)

}).then(res => {
    console.log('接受的结果', res)
})

2、Promise 构造函数的‘逆向研发’

我们先来打一个基本的架子 - 构造函数。

// 毫无疑问, Promise是一个构造函数。
// 直接上构造函数的基本配置:大写函数,原型方法

function Promise () {
    



}

// 上边我们说了,实例方法 有个 then 的方法用来接收结果的,那么 这个方法定义在 prototype上更合适

Promise.prototype.then = function () {
    // 这里接收结果    

}

架子好了之后,我们要考虑这构造函数中都有些设么东西。

A、状态 status

我们知道promise内部的三大状态: pending(等待),fulfilled(成功), rejected(失败)。promise对象的状态,从_Pending_转换为_Fulfilled_或_Rejected_之后, 这个promise对象的状态就不会再发生任何变化。如下图:

那么构造函数理应有个 状态(status)的属性;

function Promise () {

    // 默认的初始状态
    this.status = 'pending';
}

B、值(错误) value或error

有了状态 之后,要有对应的值。

function Promise () {

    // 状态
    this.status = 'pending';
    
    // 成功的值 对应的是resolve ,默认为 null
    this.value = null;
    // 失败的原因 对应的是reject,默认为null
    this.reason = null;



}

 C、构造函数传参 executor  

executor是带有 resolve 和 reject 两个参数的函数 。

说完了上边的值和错误,肯定是有两个函数分别用来处理对应的值: resolve(处理成功的值 - value),reject(处理错误的信息 - error)。注意,这两个参数都是函数。

// executor -> 是一个函数 函数包含了resolve, reject两个函数;
// 我们用promise的时候也是这样的 new Promise((resolve, reject) => {resolve(1000)})

function Promise (executor ) {

    
    if (typeof executor != 'function') {
       throw new Error(`${executor} is not a function`)
    }

    this.status = 'PENDING';// 状态
    this.value = null; // 成功 resolve 的值
    this.reason = null;// 失败 reject 的值

    this.resolve = (value) => {
    // 确保状态是 pending才能 更新状态
       if (this.status == 'pending') {
           this.status = 'fulfilled';
           this.value = value;
       }

   }

   this.reject = (reason) => {
    // 确保状态是 pending才能 更新状态
    if (this.status == 'pending') {
       this.status = 'rejected';
       this.reason = reason;
    }
  }
    
  // 执行这个传递进来的函数
  executor(this.resolve, this.reject);
}

D、then 方法

不论是成功或是失败都会调用then方法,用来接收传递过来的值。

Promise.prototype.then = function (onFulFilled, onRejected) {

    // -> 成功时传值的处理
    if (this.status == 'fulfilled') {
        if (typeof onFulFilled == 'function') {
          onFulFilled(this.value)
        }
    }
    
    // -> 失败时传值的处理
    if (this.status == 'rejected') {
       if (typeof onRejected == 'function') {
         onRejected(this.reason)    
       }
    }
}

好了,现在主体的方法基本处理完了。现在我们来试验一下,我们写的这个promise

// 场景一: -> 用来处理 同步的代码
new Promise((resolve, reject) => {
    console.log('promise 执行了')  // -> 输出成功,同步代码通过了
})


// 但是我们一开始就说,promise的用处在于处理异步操作。
// 场景二: -> 用来处理 异步代码
new Promise((resolve, reject) => {
    settimeout(() => {
        // 异步代码操作 以成功传值为例
        resolve(1000)
    },1000)
}).then(res => console.log(res))
// -> 结果是 没有任何输出结果。

处理异步的时候,我们目前的代码并不能执行。为什么呢?我们要注意,异步代码并不会立即执行。而是在主体代码执行之后,才会来处理异步代码。所以,在一开始执行then方法的时候,promise的状态保持在 pending。那么既然pending,因此then方法即没有调用onFulfilled也没有调用onRejected。所以这个值根本就不会被捕获到。

那么如何处理异步代码?

F、处理异步代码

上边说到,then执行的时候 promise的状态始终在 pending. 所以,并不能处理回调。那么我们在他 pending的时候用一个数组,把它存起来,在 resolve 的时候执行就可以了。

function promise (executor) {
    this.status = 'pending';

    this.value = null;
    this.reason = null;
    // 在这里加上两个暂存数组 分别存储 resolve的异步函数和reject的异步函数
    this.onFulFilledAry = [];
    this.onRejectedAry = [];

    this.resolve = value = > {
          if (this.status == 'pending') {
             this.value = value;
             this.status = 'fulfilled'  
             // 在这里执行所有resolve暂存的回调
             this.onFulFilledAry.forEach(fn => fn(value)) 
          }
    }

    this.reject = reason => {
        if (this.status == 'pending') {
            this.status = 'rejected';
            // 在这里执行所有的reject的暂存回调
            this.onRejectedAry.forEach(fn => fn(reason))
            this.reason = reason;
        }
    }

    executor(this.resolve, this.reject);
}

Promise.prototype.then = function (onFulFilled, onRejected) {

   // 这里主要处理异步代码的执行
    
    if (this.status == 'pending') {
        if (typeof onFulFilled == 'function) {
            // 成功的回调函数存进暂存的数组中
            this.onFulFilledAry.push(onFulFilled )
        }

        if (typeof onRejected== 'function) {
            // 失败的回调函数存进暂存的数组中
            this.onRejectedAry.push(onRejected)
        }
    }
    
    
  // 下边的resolve和reject的状态的代码不写了
  ......
}

我们再次测试一下,异步的代码。

let p = new Promise((resolve, reject) => {
       settimeout(() => {
            resolve(1000)
       }, 1000)
})
 
p.then(res => console.log(res)) // -> 成功输出 1000

至此,Promise已经支持了异步操作,promise最基本的功能已经基本实现了。

至于还有其他的API,比如 promise.all promise.race等等。后续有时间继续探究分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章