探究Promise

Posted by franki on March 8, 2020

要分析Promise之前,先来了解下Promise的应用场景。

先说结果:Promise非常适用于异步场景,或者其他需要状态保存的场景。

我们来看看MDN对于Promise的定义:

Promise 对象用于表示一个异步操作的最终完成(或失败),及其结果值

看看具体使用:

const promise1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('foo');
  }, 300);
});

promise1.then(function(value) {
  console.log(value);
});

// 300ms后输出'foo'

以上是Promise最简单的使用方式

下面主要介绍为何Promise能实现上述的效果以及更多复杂的场景。

好的,就像大部分的视频网站播放的游戏广告一样(开局一条狗,装备全靠捡),先附上流程图(盗用MDN的图)

promises

Promise 标准解读

  1. 只有一个then方法,没有catch,race,all等方法
  2. then方法返回一个新的Promise
  3. Promise的初始状态为pending,可以转换为resoved或者rejected,状态一旦确定,无法更改

一步一步实现Promise

构造函数

Promise构造函数接收一个executor函数,executor函数执行完异步操作后,调用它的两个参数resolve和reject

function Promise(executor) {
  var self = this;
  self.status = 'pending'; // Promise 当前的状态
  self.value = undefined; // Promise的值
  self.onResolveCb = []; // resolve回调函数集,Promise执行完毕时可能resolve还未调用(异步),也可能存在多个回调
  self.onRejectCb = []; // reject回调函数集,Promise执行完毕时可能reject还未调用(异步),也可能存在多个回调
  
  function resolve(value) {
    if (self.status === 'pending') {
      self.status = 'resolved';
      self.value = value;
      for (var i = 0; i < self.onResolveCb.length; i++) {
        self.onResolveCb[i](value);
      }
    }
  }
  
  function reject(reason) {
    if (self.status === 'pending') {
      self.status = 'rejected';
      self.reason = reason;
      for (var i = 0; i < self.onResolveCb.length; i++) {
        self.onRejectCb[i](reason);
      }
    }
  }
  
  try {
    executor(resolve, reject); // 执行executor函数,并传入相应的参数
  } catch(e) {
    reject(e);
  }
}

上面的代码主要做了两件事:

1 定义Promise的状态及其状态值

2 通过定义的内部函数去修改由pending转换为resolved或者rejected

原型上的then方法

Promise对象上有一个then方法,这个方法主要用来注册Promise状态确定后的回调。

而且每个Promise对象都可以在其上多次调用then方法,而每次调用then返回的Promise状态取决于上一次调用then方法返回的结果,所以then不能返回this。

看看then是如何实现的

// then方法接收两个参数,onFulfilled onRejected分别为Promise成功和失败的回调
Promise.prototype.then = function(onFulfilled, onRejected) {
  var self = this;
  var promise2;
  
  // 防止传入的参数不是函数,需要做以下处理
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(v) {return v};
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {return r};
  
  if (self.status === 'resolved') {
      return promise2 = new Promise(function(resolve, reject) {
        try {
          var x = onFulfilled(self.value);
          if (x instanceof Promise) { // 如果x是Promise对象,直接将它作为primise2的结果
              x.then(resolve, reject);
          }
          resolve(x);
        } catch(e) {
          reject(e);
        }
      });
  }
  
  if (self.status === 'rejected') {
      return promise2 = new Promise(function(resolve, reject) {
        try {
          var x = onRejected(self.reason);
          if (x instanceof Promise) { // 如果x是Promise对象,直接将它作为primise2的结果
              x.then(resolve, reject);
          }
        } catch(e) {
          reject(e);
        }
      });
  }
  
  if (self.status === 'pending') {
      return promise2 = new Promise(function(resolve, reject) {
        self.onResolveCb.push(function() {
          try {
            var x = onFulfilled(self.value);
            if (x instanceof Promise) { // 如果x是Promise对象,直接将它作为primise2的结果
                x.then(resolve, reject);
            }
          } catch(e) {
            reject(e);
          }
        });
        self.onRejectCb.push(function() {
          try {
            var x = onRejected(self.reason);
            if (x instanceof Promise) { // 如果x是Promise对象,直接将它作为primise2的结果
                x.then(resolve, reject);
            }
          } catch(e) {
            reject(e);
          }
        });
      });
  }
};

Promise.prototype.catch = function(onReject) {
  return this.then(null, onReject);
};

以上一个Promise就已经书写完毕(race,all自行google)

好了,现在我们测试下:

1 最简单的

promise1

2 处理异步

promise2

3 输出拒绝状态的Promise

promise3

4 then链式调用

promise4

5 处理异常

promise5

至此,Promise的探究到此结束,剩余的race、all方法可以自行探索。

总结一下,Promise在解决异步方面确实好用,但是Promise也不是没有缺点,比如说性能问题,then链过长,如何停止Promise。这些问题,留给大家自行思考!