Promise原理篇

319 阅读3分钟

ES6的发布,解决了以前的js的大部分问题,但要说ES6中提出的哪部分内容最火?毫无疑问Promise肯定是备选之一。

那Promise是什么呢?它又解决了什么问题呢?它有什么应用和方法呢?

下面我们一一叙述。

首先我们先了解Promise解决的什么问题,然后才会对Promise有一个大概的方向,然后叙述Promise的原理,使得对Promise有个直观的理解,最后叙述Promise的方法与应用,加深对Promise的理解。最后一点会在另一篇中叙述。

Promise解决了什么问题

我们不直接阐述结论。

首先我们有一个需求,我们要实现call10这个函数,但是call10的实现依赖call9(,而call9()的实现依赖call8,......,call1的实现依赖call。如果在es6未提出之前,那么代码将会是:(这里使用es6的箭头函数优化代码)

function call() {
    call1 = () => {
        call2 = () => {
            call3 = () => {
                call4 = () => {
                    call6 = () => {
                        call7 = () => {
                            call8 = () => {
                                call9 = () => {
                                    call10 = (val) => {
                                        return  val
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

看着这个代码是不是很吓人,如果是call100呢,脑补一下画面,会不会感觉很崩溃。

而上述这种代码风格被称之为回调地狱问题,确实很形象。

上面的代码如果用Promise之后呢?

我们来看一下:

let call = new Promise(function (resolve, reject) {
    resolve()
})
​
call.then()//实现call1
    .then()//实现call2
    .then()//实现call3
    .then()//实现call4
    .then()//实现call5
    .then()//实现call6
    .then()//实现call7
    .then()//实现call8
    .then()//实现call9
    .then()//实现call10

看着是不是比第一个代码更舒服,更方便。

确实Promise的提出主要是为了解决回调地狱的问题。

解决回调地狱,解决回调地狱,解决回调地狱

Promise是什么

我们先看一个Promise实例:

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

通过这个实例我们可以得到以下信息:

  • Promise是一个类(构造函数)
  • Promise是一个容器,里面包含一个函数,这个函数一共两个参数,分别是resolve和reject
  • Promise主要是用来解决异步的任务,也就是异步回调

好,我们现在对于Promise算是有个非常基础的理解了。

Promise的三种状态

Promise 对象存在以下三种状态:

  • Pending(进行中)
  • Fulfilled(已成功)(后面我们用resolve代替)
  • Rejected(已失败)

这三种状态之间只存在两种转化关系,分别是:

  • Pending ----> Resolve
  • Pending ---->Reject

这样我们就可以得出Promise的第一个特点:

  • Promise的状态一旦确定(Resolve,Reject),就无法再改变。

同时我们也应该注意一下几点:

  • 如果Promise中为调用Resolve或者Reject,那它会处于Pending的状态。无法调用then函数。
  • Promise内部的状态不受外界的影响。
  • Promise无return函数,是通过Resolve或者Reject保存数值。

Promise源码

const [Pedding, Resolved, Rejected] = ['pedding', 'resolved', 'rejected'];
​
function MyPromise(fn) {
    var self = this;
    this.state = Pedding;
    this.value = null;
    this.ResolvedCallbacks = [];
    this.RejectedCallbacks = [];
​
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
    
}
​

下面我们来解析一下源码。

首先 constructor这部分内容就先不讲,有兴趣的可以去看阮大佬的es6教程。

我们知道Promise主要有三部分:

  • 状态(resolve/reject)(初始状态为Pending)
  • Resolve成功及内容
  • Reject失败及内容

所以源码初始化就是以上三个部分的内容:

this.state = Pedding;
this.value = null;

然后分别针对Resolve和Reject作了分别的定义,并包含执行错误的情况。

if (self.state === Pedding) 

上述这行if语句保证了Promise的状态转换的唯一性。

引用

Promise 对象

面试官对不起!我终于会了Promise...(一面凉经泪目)