Promise原理实现

本文不解释什么是Promise,主要是用两个由浅入深的例子实现以]promise的大致功能。

先来看一下promise的简单用法:

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

    resolve('成功');

}, ()=>{})

p.then((data) => {console.log(data);},(err) => {});

来写一个简易版的promise:

首先,需要创建一个构造函数promise,创建一个promise类,在使用的时候传入了一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。

promise的状态改变是不可逆的,开始是pending状态,成功后转为resolved状态,失败则转为rejected,这里我们用status来标识状态的变化

 

源码如下(后面会举一个例子分析执行过程):

class Promise {

  constructor(executor) {

    this.status = 'pending'; // 默认状态是等待状态

    this.value = undefined; // 用来存放返回的结果值

    this.errorStr = undefined; // 用来存放错误信息

    this.resolveFuncs = []; //存放成功的回调

    this.rejectFuncs = []; //存放失败的回调

    let resolve = (data) => { // 成功调用

      if (this.status === 'pending') {

        this.value = data;

        this.status = "resolved";

        this.resolveFuncs.forEach(fn => fn());

      }

    }


    let reject = (errorStr) => { // 失败调用

      if (this.status === 'pending') {

        this.errorStr = errorStr;

        this.status = 'rejected';

        this.rejectFuncs.forEach(fn => fn());

      }

    }

    try {

      executor(resolve, reject); // 执行

    } catch (e) {

      reject(e); //失败

    }
  }

  // then方法是promise的最基本的方法,返回的是两个回调,一个成功的回调,一个失败的回调

  then(onFulFilled, onRejected) {

    if (this.status === 'resolved') { // 成功状态的回调

      onFulFilled(this.value);

    }

    if (this.status === 'rejected') { // 失败状态的回调

      onRejected(this.errorStr);

    }

  }

}

还是上面那个例子:

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

    resolve('成功');

}, ()=>{})

p.then((data) => {console.log(data);},(err) => {});

执行过程分析:

  • 实例化一个Promise对象,传入executor,生成constructor中的属性
  • 执行resolve(‘成功’),后调用 
let resolve = (data) => { 

      if (this.status === 'pending') {

        this.value = data;

        this.status = "resolved";

        this.resolveFuncs.forEach(fn => fn());

      }

}

将结果值存到value中,并且将状态改为resolved,再执行this.resolveFuncs中保存的方法,不过这边数组内为空。

  • 执行.then(data) => {console.log(data);),调用
then(onFulFilled, onRejected) {

    if (this.status === 'resolved') { // 成功状态的回调

      onFulFilled(this.value);

    }

    if (this.status === 'rejected') { // 失败状态的回调

      onRejected(this.errorStr);

    }

  }

将保存的value作为回调函数的参数,执行回调函数。

  • 结束,返回的 p 是一个resolved状态的Promise

简易版的Promise实现看完了,现在来看一个复杂版的例子。该例子还实现了Promise的链式调用。

老样子,贴上源码,后面再举一个小题目分析一下执行过程

class Promise {

  constructor(executor) {

    this.status = 'pending'; // 默认状态是等待状态

    this.value = ''; // 用来存放返回的结果值

    this.errorStr = ''; // 用来存放错误信息



    this.resolveFuncs = []; // 存放成功的回调

    this.rejectFuncs = []; // 存放失败的回调



    let resolve = (data) => { // 成功调用

      if (this.status === 'pending') {

        this.status = 'resolved';

        this.value = data;

        this.resolveFuncs.forEach(func => func())

      }

    }



    let reject = (data) => { // 失败调用

      if (this.status === 'pending') {

        this.status = 'rejected';

        this.errorStr = data;

        this.rejectFuncs.forEach(func => func())

      }

    }



    try {

      executor(resolve, reject)

    } catch (error) {

      reject(error)

    }

  }

  // 成功的回调与失败的回调

  then(resolveFunc, rejectFunc) {

    resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;

    rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {

      throw err

    };

    let promise2;

    if (this.status === 'resolved') {

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

        setTimeout(() => { // 写一个宏任务

          try {

            let x = resolveFunc(this.value) // 执行回调

            resolvePromise(promise2, x, resolve, reject) // 对结果进行处理

          } catch (error) {

            reject(error)

          }

        }, 0);

      })

    }



    if (this.status === 'rejected') {

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

        setTimeout(() => {

          try {

            let x = rejectFunc(this.errorStr)

            resolvePromise(promise2, x, resolve, reject)

          } catch (error) {

            reject(error)

          }

        }, 0);

      })

    }



    if (this.status === 'pending') {

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

        this.resolveFuncs.push(() => { // 将成功的回调存入数组

          setTimeout(() => {

            try {

              let x = resolveFunc(this.value)

              resolvePromise(promise2, x, resolve, reject)

            } catch (error) {

              reject(error)

            }

          }, 0);

        });

        this.rejectFuncs.push(() => {

          setTimeout(() => {

            try {

              let x = rejectFunc(this.errorStr)

              resolvePromise(promise2, x, resolve, reject)

            } catch (error) {

              reject(error)

            }

          }, 0);

        });

      })

    }

    return promise2;

  }

  // catch 只传入reject的回调

  catch (onRejected) {

    return this.then(null, onRejected);

  }

}



function resolvePromise(promise2, x, resolve, reject) {

  if (promise2 === x) { // 不能返回自身

    return reject(new TypeError('循环引用'))

  }

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 判断返回的是否为对象或者函数

    try {

      let then = x.then; // 取then方法

      if (typeof then === 'function') { // 如果then方法存在,那么就是返回了一个promise

        then.call(x, y => { // 调用then方法

          resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject

        }, e => {

          reject(e)

        })

      } else {

        resolve(x)

      }

    } catch (error) {

      reject(x)

    }

  } else {

    resolve(x)

  }

}



举一个例子:

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

  resolve(2)

}).then(data => {

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

    resolve(data+1)

  })

}).then(data => {

  console.log(data+1)

})

 

执行过程分析:

  • 先实例化一个promise1对象,将executor传入,执行constructor构造函数

  • 执行resolve(2)

let resolve = (data) => { // 成功调用

      if (this.status === 'pending') {

        this.status = 'resolved';

        this.value = data;

        this.resolveFuncs.forEach(func => func())

      }

    }

函数会将promise1的status改为resolved,将2存入value中,执行resolveFuncs数组中的方法,不过这的resolveFuncs为空

  • 执行promise1.then()
then(resolveFunc, rejectFunc) {

    resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;

    rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {

      throw err

    };

    let promise2;

    if (this.status === 'resolved') {

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

        setTimeout(() => { // 写一个宏任务

          try {

            let x = resolveFunc(this.value) // 执行回调

            resolvePromise(promise2, x, resolve, reject) // 对结果进行处理

          } catch (error) {

            reject(error)

          }

        }, 0);

      })

}

(....省略.....)

return promise2;

}

因为status为resolved,则为创建一个promise2,然后写一个setTimeout加入到宏任务队列中,在同步任务和微任务都执行结束后执行,这边称它为宏任务1。宏任务1主要是将value值传入回调函数resolveFunc中,得到一个结果x。然后使用resolvePromise函数x进行分析,如果是值则直接resolve,该例子中是返回一个新的promise,所以就再传入resolvePromise函数中,递归得到结果。

这一步最后是返回promise2

在resolvePromise函数中的执行后面再分析。

  • 上一步返回的promise2这边为了不混乱,称为promiseNew。执行promiseNew.then() 方法,因为promiseNew还未resolve(resolve在宏任务1中),因此状态还是pending。

执行以下代码:

then(resolveFunc, rejectFunc) {

    resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;

    rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {

      throw err

    };

    let promise2;

    if (this.status === 'pending') {

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

        this.resolveFuncs.push(() => { // 将成功的回调存入数组

          setTimeout(() => {

            try {

              let x = resolveFunc(this.value)

              resolvePromise(promise2, x, resolve, reject)

            } catch (error) {

              reject(error)

            }

          }, 0);

        });

        this.rejectFuncs.push(() => {

          setTimeout(() => {

            try {

              let x = rejectFunc(this.errorStr)

              resolvePromise(promise2, x, resolve, reject)

            } catch (error) {

              reject(error)

            }

          }, 0);

        });

      })

    }

    return promise2;

}

写一个方法 ()=>{} push进resolveFuncs数组中,这个方法中是一个setTimeout宏任务,这边称它为宏任务2,但是还未加入队列,因为这个方法只是存入数组,还没执行。

到这,同步任务和微任务都执行完毕,开始执行宏任务。

  • 这时宏任务队列中有一个宏任务1,则执行宏任务1。
setTimeout(() => { // 写一个宏任务

          try {

            let x = resolveFunc(this.value) // 执行回调

            resolvePromise(promise2, x, resolve, reject) // 对结果进行处理

          } catch (error) {

            reject(error)

          }

        }, 0);

根据箭头函数的this指向,这边的this.value=2。返回的x是一个resolved状态的promise,执行resolvePromise函数,该函数接受四个参数,一个是promise2,也就是promiseNew,一个是x,还有promiseNew的resolve和reject方法。在函数中会调用resolve(data+1),因此返回的x中的状态是resolved,value为3

接下来看一下resolvePromise函数

function resolvePromise(promise2, x, resolve, reject) {

  if (promise2 === x) { // 不能返回自身

    return reject(new TypeError('循环引用'))

  }

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 判断返回的是否为对象或者函数

    try {

      let then = x.then; // 取then方法

      if (typeof then === 'function') { // 如果then方法存在,那么就是返回了一个promise

        then.call(x, y => { // 调用then方法

          resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject

        }, e => {

          reject(e)

        })

      } else {

        resolve(x)

      }

    } catch (error) {

      reject(x)

    }

  } else {

    resolve(x)

  }

}

函数中对x与promise2进行判断,不能返回自己,否则就是循环引用。

然后判断x是否存在,是否为对象。这边的x是一个promise对象,因为会取到它的then方法,执行

 then.call(x, y => { // 调用then方法

          resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject

        }, e => {

          reject(e)

        })

传入x对象,与resolve和reject回调。这边执行.then方法,会再进入then中status为resolved的逻辑中,再创建一个promise2(这就是为什么我把前面的promise2改名为promiseNew),再生成一个setTimeout宏任务1-1,然后return promise2。这是再去执行宏任务1-1,当前的this.value的this指向的是x这个promise,因此value为3,传入回调函数,函数中再去执行resolvePromise,这边要注意的是,resolve和reject一直都是promiseNew传过来的。最后如果判断x为一个值,resolve(x)

  • 就到了promiseNew.then()最后这一步,上一步resolve的时候会将promiseNew的状态改为resolved,并且将value设为3,再去执行resolveFuncs数组中的函数。

这时会发现,resolveFuncs数组中刚好有一个函数,就是那个包装了宏任务2的函数。

执行后,将宏任务2加入队列中。

发现以及没有同步任务与微任务了,则执行宏任务2

setTimeout(() => {

            try {

              let x = resolveFunc(this.value)

              resolvePromise(promise2, x, resolve, reject)

            } catch (error) {

              reject(error)

            }

          }, 0);

会发现和宏任务1其实差不多,所以不再赘述。

不同的只是这个x并不是一个对象,而是undefind。

以上就是实现了。

参考文章:

https://juejin.im/post/5afe6d3bf265da0b9e654c4b#heading-10

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值