felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

在 ES5 中实现 ES6 中的 Promise 对象 #130

Open felix-cao opened 5 years ago

felix-cao commented 5 years ago

《Promise 基础知识》 中,我们聊了聊 ES6 中的 PromisePromise 改善了异步回调的嵌套问题,让我们这些程序员写异步程序的体验非常棒!那么在 ES5 下我们可不可以实现一个 Promisecode 呢?

一、分析 ES6 中 Promise

1.1、 Promise 是一个构造函数

Promise 是一个构造函数,接受一个函数作为参数,函数里面有两个参数 resolvereject 分别作为执行成功和执行失败的函数

var promise = new Promsie(fn)

function fn(resolve,reject) {
  if(/*异步执行成功*/){
    resolve(value);
  } else {
    reject(error);
  }
}

《Promise 基础知识》 ,我们知道 resolvereject 是两个函数,由 JavaScript 引擎提供,这里得我们自己实现

1.2、then 承诺

如果说 fn 中定义的是指定承诺所需完成的事, 那么 then 方法是设置承诺是否实现的标准。then 接受两个函数作为参数,第一个函数是完成承诺后要继续干的事,第二个参数是承诺失败后要继续干的事。二承诺的标准是否完成是在 fn 里定义的。

我们还可以换个方式来理解这里的 then 方法,我们知道 Promise 对象的状态改变有两种:

promise.then(
  function(success) {},
  function(error) {}
)

二、雏形

现在我们来实现 Promise 的雏形,定义一个构造函数:

function Promise(fn) {
  var resolveCB = null;
  var rejectCB = null;

  this.then = function(onResolved, onRejected) {
    resolveCB = onResolved;
    rejectCB = onRejected;
  }

  this.resolve = function(value) {
    resolveCB && resolveCB(value);
  }

  this.reject = function(reason) {
    rejectCB && rejectCB(reason);
  }

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

在 Promise 内部我们实现了 thenresolvereject 三个方法。这里这么是为了方便我们理解,在JavaScript 的构造函数里,这么写会导致内存被占用浪费。后面会优化。

用上面的 Promise 跑下面的函数试试

var p = new Promise(fn);
p.then(function(data) {
  console.log('resolve: ', data);
}, function(data) {
  console.log('reject: ', data);
})

function fn(resolve, reject) {
  console.log('begin to execute!');
  var number = Math.random();
  if(number<=0.5) {
   setTimeout(() => {
     resolve('less than 0.5')
   })
  } else {
   setTimeout(() => {
     reject('greater than 0.5');
   })
  }
}

三、状态管理

我们知道 Promise 内部存在3个状态:pending,resolved,rejected;

function Promise(fn) {
  var state = 'pending';
  var resolveCB = null;
  var rejectCB = null;

  this.then = function(onResolved, onRejected) {
    resolveCB = onResolved;
    rejectCB = onRejected;
  }

  this.resolve = function(value) {
    if(state === 'pending') {
      resolveCB && resolveCB(value);
      state = 'resolve';
    }
  }

  this.reject = function(reason) {
   if(state === 'pending') {
     rejectCB && rejectCB(reason);
     state = 'reject';
   }
  }

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

To Be Continuing