Promise实现的基本原理(一)

前言

前几天在项目中遇到回调地狱的情况,但我在刚开始写这个项目的时候还不会用Promise,只知道它可以用来解决回调地狱,所以就用全都用回调函数解决。我是看《深入理解ES6》这本书来学Promise的,在用法和功能上讲得还是挺详细的,但在原理上没怎么讲,所以用的时候有很多地方有疑问,于是在网上找到一篇自己实现Promise的文章来理解Promise实现原理。

参考链接:www.mattgreer.org/articles/pr…

一、最简单的用法

new Promise( (resolve, reject) => {
    resolve(42)
}).then( result => {
    console.log(result)
})

// 控制台输出
// 42
复制代码

这是一段最简单的Promise使用(我们先不讨论拒绝reject,假设所有的操作都被接受resolve),很开心,终于会用Promise了(心虚~),但我在使用的时候出现了一些疑问,在我说我的疑问之前,我们先说一下函数的回调

回调函数

在js中,函数a作为参数传给另一个函数b,此时函数a就叫做回调函数。在函数b的函数体中调用传进来的函数a,此时函数a就被函数b回调了。

OK,现在我们懂什么是回调函数了,我可以说我的疑问了

二、我的疑问 —— resolvethen

首先我们先分析一下上面最简单的例子中有多少个回调函数,我把上面的代码重新写一下:

// 现在不用箭头函数,用变量直观点,这里就不写reject了
let fn1 = function (resolve) {
    resolve(42)
}

let fn2 = function (result) {
    console.log(result)
}

new Promise(fn1).then(fn2)  // 42
复制代码

new Promise(fn1).then(fn2) 这行代码可以很直观地看出这里有 2 个回调函数,但还没完,再看看fn1这个函数

let fn1 = function (resolve) {
    // resolve再这里被调用了,说明resolve是个回调函数
    resolve(42)
}
复制代码

所以现在我们的会调函数是这样的:

new Promise的时候给Promise传入了一个含有resolve这个回调函数的回调函数fn1,创建完Promise对象之后,随即调用了Promise对象中的then()方法,给then方法传入了一个回调函数fn2。(这里涉及到一点点的new操作符的作用)

好了,分析完回调函数之后我开始想不通了:

  • 疑问1:什么时候调用fn1我都不知道
  • 疑问2:我没有给fn1传回调函数啊
  • 疑问3:就先装作我知道fn1是什么时候调用的吧,那为什么在fn1调用resolve的时候,为什么看起来像调用的是我传过去给then的回调函数fn2
  • 啊?

三、实现原理

终于回到正题了,解决一下我上面的疑问。

在开始的那篇参考的文章中,作者实现了一个还算比较完整的Promise,内容有点长,我就只截取其中能够实现上面写的最简单Promise使用的原理。

function Promise(fn) {
  var state = 'pending';        // Promise当前的状态
  var value;                    // 当Promise处于接受状态时保存下来的值,这是结果
  var deferred;                 // 延期执行函数(现在还看不出来它是函数)

  function resolve(newValue) {  // Promise被接受之后执行的函数
    value = newValue;           // 保存获取到的结果
    state = 'resolved';         // 将状态改为resolved

    if(deferred) {              // 如果deffred不是undefined,已经有指向函数的引用
      handle(deferred);         // 将deffred传给handle函数处理
    }
  }

  /*
   * 当用户调用then函数时调用的处理函数
   * 等等就解释
   */
  function handle(onResolved) {
    if(state === 'pending') {   // 判断当前所处的状态,如果是padding状态,即没有被处理状态,还处于初始状态
      deferred = onResolved;    // 将onResolved回调函数保存下来,稍后执行
      return;                   // padding状态就不执行下面的语句
    }

    onResolved(value);          // 如果不是padding状态,调用onResolved,并把结果传给回调函数
  }

  /*
   * 这个是我的疑问then了
   * 这里涉及一个关键字this,涉及的内容有点多,先不说
   * 看等号后面是一个function,所以它是一个函数,并且传了一个参数,是回调函数
   * 从上面的的代码可以看出onResolved是一个回调函数
   */
  this.then = function(onResolved) {
    handle(onResolved);         // 每当调用then这个函数,就将onResolved(例子中的fn2)传给handle函数
  };
  
  /*
   * 上面全都是函数和变量的定义,只有这一行是执行函数
   * 疑问1解决:在new Promise的时候就执行了传过来的回调函数fn1
   * 疑问2解决:在调用fn1的时候传过去的回调函数是Promise的内部函数resolve
   * 疑问3解决:有点绕,我写在代码外面
   */
  fn(resolve);
}
复制代码

疑问三解决:

前两个疑问的解决请看fn的注释

我们在用Promise对象调用then函数的时候给他传了fn2,也就是onResolved是指向fn2的,然后onResolved又传给了handle,在handle里面调用了onResolved,也就是说,fn2handle里调用。

现在我们知道了fn2是在handle里被调用了。然后handle在哪里被调用

因为例子中没有异步操作,所以先不考虑padding情况:

handle函数在then函数中调用

再看回最简单的Promise用法:

let fn1 = function (resolve) {
    resolve(42)
}

let fn2 = function (result) {
    console.log(result)
}

new Promise(fn1).then(fn2)  // 42
复制代码
1. 执行new Promise(fn1)时做了什么?

当程序执行到new Promise时,Promise里面的语句fn(resolve)会马上执行,调用了我们传过去的回调函数fn1,并且给我们传回一个值叫resolve,这个值恰好又是一个回调函数(参数名resolve的指向是Promise对象中的resolve()函数,我们在这个例子中没有写任何的异步操作,所以东西几乎都是同步执行的)。我们又马上调用了resolve回调函数,并给它传入一个值42(newValue),state被置为'resolved',此时new Promise就全部执行完成。总结一下上面这段话:我们这个例子没有异步操作,所以resolve函数在new Promise的时候就被调用了完了,状态改成了'resolved'

2. 执行then(fn2)时做了什么?

例子中创建完Promise对象之后马上调用了then函数,并且传入了一个会调函数fn2(这里的then其实是一个微任务,这里先不做讨论,暂且当它是马上执行的)。调用then时,then调用了Promise对象的函数handle并且把onResolved指向了fn2,因为在new Promise的时候就已经把status改成'resolved'了,所以handle函数体里面的if语句不成立,于是就执行onResolved(value)并且把new Promise中的到的值传入,也就是说fn2handle回调了。总结上面的这段话:在我们调用then的时候调用了handle,并且把new Promise时得到的值传回给fn2回调函数

到现在疑问三解决了。

总结疑问三:

fn被调用的时候,给fn1传回了Promise的内部函数resolve,被fn1的resolve参数所接收,所以在fn1方法体内调用的resolve实际是指向Promise的内部函数resolve,并未我们给他传了42这个值,被Promiseresolve函数保存在value当中。在调用then函数的时候,value被handle传出来给fn2

终于解决完问题了!!

关于Promise的三种状态的作用,还有如何实现串行的Promise,和reject,看完那篇参考文章的还是很好理解的,里面还有很多东西,我就不写了,去看吧。

四、Promise 的使用和理解中涉及到的知识点

  • new 关键字创建对象
  • this关键字
  • 原型链
  • js和浏览器的异步机制
  • event loop

转载于:https://juejin.im/post/5caf2f51e51d456e8240dcae

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值