Promise 的实现原理

244 阅读7分钟

Promise 的实现原理

Promise 的好处就不多说了,本文采用TDD(测试驱动开发)的形式去实现一个Promise。

测试库用jest,本文不会去介绍jest的用法。

第1个测试用例

test('测试Promise是构造函数, 且原型上有then方法', () => {
  const promise = new Promise(() => {})

  expect(promise.constructor).toEqual(Promise)
  expect(promise).toEqual(expect.objectContaining({ then: expect.any(Function)}))
})

要实现的功能:

  • Promise 实例是通过构造函数的形式实现,接收的参数是一个函数
  • 其原型上有一个 then 方法,then方法接收二个参数,这两个参数都是函数

这个还是比较简单的直接上代码

实现

function Promise(fn) {}

Promise.prototype.then = function(onFulfilled, onRejected) {}

第2个测试用例

test('测试Promise构造函数的参数接收一个函数,并且会立即执行', () => {
  let called = false;
  new Promise(function () {
    called = true;
  })
  expect(called).toBeTruthy()
})

test('测试resove和reject是函数', () => {
  const mockFunction = jest.fn(function(resolve,reject) {});
  const promsie = new Promise(mockFunction)
  expect(typeof mockFunction.mock.calls[0][0] === 'function').toBeTruthy()
  expect(typeof mockFunction.mock.calls[0][1] === 'function').toBeTruthy()
})

要实现的功能:

  • Promise 构造函数的参数接收两个参数,第一个是resolve,第二个是reject,这两个都是函数
  • 在 new Promise() 时构造函数的参数是立即执行的
  • 调用 resolve() 时还可以传入一个值

第2个测试用例实现的难度一下子上去了,不能直接上代码了,我们要分析一下

分析:

我们知道 Promise 实例有三个状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败), 创建时的初始状态是pending,执行resolve()后会变成 fulfilled,并且调用resolve()时还要传一个值,这个值还要找个地方保存起来。

有了上面的分析我们知道Promise要有一个能保存状态的变量,还要又一个能保存resolve值的变量

实现

function Promise(fn) {
  
  // 保存状态的变量
  // 0 - pending
  // 1 - fulfilled
  // 2 - rejected
  this._state = 0;

  // 保存值的变量
  this._value = null;

  // 封装一个函数调用fn
  doResolve(fn, this)
}

function doResolve(fn, promise) {
    var done = false;

    // 立即调用fn,并且传入 resolve 和 reject
    var res = fn(function (value) {
      if (done) return;
      done = true;
      resolve(promise, value)
    }, function(reason) {
      
    })
}


  function resolve(self, newValue) {
    // 更改状态为fulfilled
    self._state = 1

    // 保存值
    self._value = newValue
  }

第3个测试用例

test('测试promise resolve 了调用then方法', (done) => {
  new Promise(function (resolve) {
    process.nextTick(function () {
      resolve({ value: 1 })
    });
  }).then(result => {
    expect(result).toEqual({ value: 1 })
    done()
  })
})

要实现的功能:

  • 调用 resolve(1) 后,p1.then的回调函数要能够执行,并且能够接收到resolve传过来的值(本例中就是1)

分析:

resolve() 执行后要能够执行then的回调函数,首先想到的是要把then的回调函数保存到一个地方,等到promise实例的状态变为fulfilled时,就去执行该回调函数,为了能够保存then的回调函数,我们需要定义一个新的类Handler。

实现

function Promise(fn) {
  
  // 保存状态的变量
  // 0 - pending
  // 1 - fulfilled
  // 2 - rejected
  // 3 - adopted the state of another promise
  this._state = 0;

  this._deferredState = 0;

  // 保存值的变量
  this._value = null;

  this._deferreds = null

  // 封装一个函数调用fn
  doResolve(fn, this)
}


Promise.prototype.then = function(onFulfilled, onRejected) {
    
    // new Handler时把then的回调函数保存到Handler实例
    // 还需要一个handle函数来关联Promise实例和Handler实例
    handle(this, new Handler(onFulfilled, onRejected))
}

function Handler(onFulfilled, onRejected) {
    this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
    this.onRejected = typeof onRejected === 'function' ? onRejected : null;
}

function handle(self, deferred) {
    if (self._state === 0) {
      if (self._deferredState === 0) {
        self._deferredState = 1
        self._deferreds = deferred; // 把handler实例挂到promise对象的_deferreds属性上
        return;
      }
    }

    handleResolved(self, deferred)
}

// 真正调用then中回调的地方
function handleResolved(self, deferred) {
  setTimeout(function() {
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected

    if (cb === null) {
      return
    }

    var ret = cb(self._value)
  }, 0)
}

function resolve(self, newValue) {
    // 更改状态为fulfilled
    self._state = 1

    // 保存值
    self._value = newValue

    // 处理resolve后的逻辑
    finale(self)
}

function finale(self) {
  if (self._deferredState === 1) {
    handle(self, self._deferreds) // handle中会进入handleResolved方法
    self._deferreds = null
  }
}

function doResolve(fn, promise) {
    var done = false;

    // 立即调用fn,并且传入 resolve 和 reject
    var res = fn(function (value) {
      if (done) return;
      done = true;
      resolve(promise, value)
    }, function(reason) {
      
    })
}

此时已经实现了一个最简单Promise,还有一个最重要的功能没有实现就是链式调用,继续完善

第4个测试用例

test('测试promise.then返回一个新的promise实例', (done) => {
  const promise1 = new Promise(function (resolve) {
    process.nextTick(function () {
      resolve({ value: 1 })
    });
  })
  const promise2 = promise1.then(result => {
    done()
  })
  expect(promise2 instanceof Promise).toBeTruthy()
})

test('测试下一个then能够接收到上一个then的返回值', (done) => {
  const promise1 = new Promise(function (resolve) {
    process.nextTick(function () {
      resolve({ value: 1 })
    });
  })
  const promise2 = promise1.then(result => {
    return { value: 2 }
  })
  promise2.then(result => {
    expect(result).toEqual({ value: 2 })
    done()
  })
})

分析: 如果p1.then()的返回值仍然是一个Promise实例,我们就可以实现链式调用了,并且p2.then的回调能够接收上一个then返回的值

要实现功能:

  • Promise.prototype.then 返回一个新的Promise实例
  • 下一个then能够接收到上一个then的返回值

实现


function noop() {}

Promise.prototype.then = function(onFulfilled, onRejected) {
    
    var res = new Promise(noop)

    // new Handler时把then的回调函数保存到Handler实例
    // 把then中返回的promise也保存到Handler实例
    // 还需要一个handle函数来关联Promise实例和Handler实例
    handle(this, new Handler(onFulfilled, onRejected, res))

    return res
}

function Handler(onFulfilled, onRejected, promise) {
    this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
    this.onRejected = typeof onRejected === 'function' ? onRejected : null;
    this.promise = promise;
}


// 真正调用then中回调的地方
function handleResolved(self, deferred) {
  setTimeout(function() {
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected

    if (cb === null) {
      return
    }

    var ret = cb(self._value)

    // 用上一个then返回的promise,和then回调函数的返回值作为参数再调用resolve
    resolve(deferred.promise, ret)

  }, 0)
}

上面就实现了then的链式调用

第5个测试用例

test('测试嵌套promise', (done) => {
  let resolveA, resolveB, resolveC
  const A = new Promise(function (resolve, reject) {
    resolveA = resolve;
  });
  const B = new Promise(function (resolve, reject) {
    resolveB = resolve;
  });
  const C = new Promise(function (resolve, reject) {
    resolveC = resolve;
  });
  resolveA(B);
  resolveB(C);
  resolveC('foo');
  A.then(result => {
    expect(result).toBe('foo')
    done()
  })
})

要实现的功能

  • resolve的值是一个promise对象时,此promise的决议结果要依赖上一个promise的决议

实现

var LAST_ERROR = null;
var IS_ERROR = {};

function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

function resolve(self, newValue) {

    // 判断newValue是promise对象
    if (newValue && typeof newValue === 'object') {

      var then = getThen(newValue)

      if (then === self.then && newValue instanceof Promise) {
        self._state = 3
        self._value = newValue
        finale(self)
        return
      }
    }


    // 更改状态为fulfilled
    self._state = 1

    // 保存值
    self._value = newValue

    // 处理resolve后的逻辑
    finale(self)
}

function handle(self, deferred) {

    // 如果resolve() 的是一个prmise对象
    while(self._state === 3) {
      self = self._value
    }

    if (self._state === 0) {
      if (self._deferredState === 0) {
        self._deferredState = 1
        self._deferreds = deferred; // 把handler实例挂到promise对象的_deferreds属性上
        return;
      }
    }

    handleResolved(self, deferred)
}

上面已经实现了一个可以链式调用,且支持 resolve Promise对象的功能,下面我们把reject的功能加上,整理出来一份完整代码


function noop() {}


var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

function Promise(fn) {
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  if (fn === noop) return
  doResolve(fn, this)
}


Promise.prototype.then = function(onFulfilled, onRejected) {

  var res = new Promise(noop)
  handle(this, new Handler(onFulfilled, onRejected, res))
  return res
}

function Handler(onFulfilled, onRejected, promise) {
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}

function resolve(self, newValue) {

  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    )
  }

  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    var then = getThen(newValue)
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR)
    }
    if (then === self.then && newValue instanceof Promise) { // 处理resolve(promise)
      self._state = 3
      self._value = newValue
      finale(self)
      return
    } else if (typeof then === 'function') { // 处理传给resolve的是thenable 对象({ then: function })
      doResolve(then.bind(newValue), self)
    }
  }

  self._state = 1
  self._value = newValue
  finale(self)
}

function reject(self, newValue) {
  self._state = 2;
  self._value = newValue;
  if (Promise._onReject) {
    Promise._onReject(self, newValue);
  }
  finale(self);
}

function finale(self) {
  if (self._deferredState === 1) {
    handle(self, self._deferreds);
    self._deferreds = null
  }
  if (self._deferredState === 2) {
    for (var i = 0; i < self._deferreds.length; i++) {
      handle(self, self._deferreds[i])
    }
    self._deferreds = null
  }
}

function handle(self, deferred) {
  while(self._state === 3) {
    self = self._value
  }

  if (self._state === 0) {
    if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      return;
    }

    /***
     * 此情况是针对如下场景
     * const p = new Promise() 
     * p.then()
     * p.then()
     * 
     ***/
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    self._deferreds.push(deferred);
    return;
  }

  handleResolved(self, deferred)
}

function handleResolved(self, deferred) {
  setTimeout(function() {
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;

    if (cb === null) {
      if (self._state === 1) {
        resolve(deferred.promise, self._value);
      } else {
        reject(deferred.promise, self._value);
      }

      return
    }

    var ret = cb(self._value)
    resolve(deferred.promise, ret)
  }, 0)
}

function doResolve(fn, promise) {
  var done = false;
  var res = fn(function (value) {
    if (done) return;
    done = true;
    resolve(promise, value)
  }, function(reason) {
    if (done) return;
    done = true;
    reject(promise, reason);
  })
}

Promise的静态方法 all/resolve/reject/reae 的实现

function valuePromise(value) {
  var p = new Promise(noop)
  p._state = 1
  p._value = value
  return p
}

Promise.resolve = function (value) {
  if (value instanceof Promise) return value 
  return valuePromise(value)
}

Promise.reject = function(value) {
  return new Promise(function(resolve, reject) {
    reject(value)
  })
}

Promise.all = function(arr) {
  return new Promise(function (resolve, reject) {
    if (arr.length === 0) return resolve([]) 
    var remaining = arr.length

    function res(i, val) {
      if (val && (typeof val === 'object' || typeof val === 'function')) {
        if (val instanceof Promise && val.then === Promise.prototype.then) {

          if (val._state === 1) return res(i, val._value)
          if (val._state === 2) reject(val._value)

          // 循环调用then方法直到决议
          val.then(function(val) {
            res(i, val)
          }, reject)
        return
        }
      }

      // 成功决议
      args[i] = val

      // 所有promise全部成功决议
      if (--remaining === 0) {
        resolve(args)
      }

    }

    for(var i = 0; i < arr.length; i++) {
      res(i, arr[i])
    }
  })
}


Promise.race = function (values) {
  return new Promise(function (resolve, reject) {
    values.forEach(function(value) {
      Promise.resolve(value).then(resolve, reject)

    })
  })
}

测试用例的执行结果:

alt 属性文本

至此我们实现了一个Promise