实现自己的Promise polyfill

功能清单:

  1. Promise.prototype.then()
  2. Promise.prototype.catch()
  3. Promise.reject()
  4. Promise.resolve()
  5. Promise.all()
  6. Promise.race()
  7. Promise.prototype.finally()

参考:Prmoises A+规范

具体实现

function Promise(fn) {
  var _this = this; 
  var callback = null;
  this._value = null; // 存放resolve(解决)的结果
  this._reason = null; // 存放reject(拒绝)的原因
  this._onResolveds = []; // 存放resolve前调用then时传入的函数onResolved(可能多次调用then,所以是数组)
  this._onRejecteds = []; // 存放reject前调用catch时传入的函数onRejected(可能多次调用catch,所以是数组)
  this._status = "pending";

  function resolve(val) {
    if (_this._status !== 'pending') { // 若status已经改变为"resolved"或"rejected",回调直接在then内处理
      return
    }
    _this._value = val;
    _this._status = "resolved";
    while (callback = _this._onResolveds.shift()) { // 按注册顺序依次执行回调
      callback(val)
    }
  }

  function reject(reason) {
    if (_this._status !== 'pending') {
      return
    }
    _this._reason = reason;
    _this._status = "rejected";
    while (callback = _this._onRejecteds.shift()) {
      callback(reason)
    }
  }

  try {
    fn(resolve, reject)
  } catch (err) {
    reject(err)
  }
}

then (重点)

  Promise.prototype.then = function (onResolved, onRejected) { 
  // 规范:如果 onFulfilled 不是函数,其必须被忽略
  // 这里,若onResolved, onRejected 不是函数,则用一个过渡性的函数代替 
  onResolved = typeof onResolved === 'function'? onResolved:function(value) {
    return value;  // 将value原封不动地传给下一个then,相当于跳过(忽略)本轮then的onResolved
  }
  onRejected = typeof onRejected === 'function'? onRejected:function(err) {
    throw err;  // 同上,相当于跳过(忽略)本轮then的onRejected
  }

  var _this = this  
  var promise2  // then始终返回一个Promise实例,用于链式调用。

  if (_this._status === "resolved") {
    promise2 = new Promise(function (resolve, reject){
      setTimeout(function() { // 确保onResolved 和 onRejected 方法异步执行。下面的setTimeout同理
        try {
          var x = onResolved(_this._value) 
          resolvePromise(promise2, x, resolve, reject) // resolvePromise内执行promise2的resolve和reject
        } catch (e) {
          reject(e)
        }
      }, 0);
    })
  }
  if (_this._status === "rejected") {
    promise2 = new Promise(function (resolve, reject){
      setTimeout(function() {
        try {
          var x = onRejected(_this._reason)
          resolvePromise(promise2, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }, 0);
    })
  }
  if (_this._status === "pending") {
    // resolve或reject前调用then的话,将回调推入队列
    promise2 = new Promise(function (resolve, reject) {
      _this._onResolveds.push(function () {
        setTimeout(function() {
          try {
            var x = onResolved(_this._value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      });
      _this._onRejecteds.push(function () {
        setTimeout(function() {
          try {
            var x = onRejected(_this._reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      });
      
    })
  }

  return promise2
};

根据onResolved/onRejected的返回值 x 的不同情况,调用promise2的resolve和reject

function resolvePromise (promise2, x, resolve, reject) {
  if(promise2 === x) { // 防止引用同一个promise,导致死循环
    return reject(new TypeError('循环引用'));
  }
  var called = false  // 防止多次调用
  if (x!== null && (typeof x ==='object' ||typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') {  
        // x 是一个定义了 then 方法的对象或函数,即thenable

        then.call(x, function(y) { // 这里规范是这样说明的:这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。

          if (called) return   // 确保resolve和reject,只执行其中一个
          called = true;
          resolvePromise(promise2, y, resolve, reject) // 如果x是thenable,则继续调用resolvePromise,直到 onResolved/onRejected 的返回值不是 thenable

        }, function(err) {
          if (called) return
          called = true;
          reject(err);
        })
      } else {
        resolve(x)  // 如果 x 不属于 thenable, 则把x作为返回值.
      }
    } catch (e) {
        if (called) return
        called = true;
        reject(e)
    }
  } else {  //普通值
    resolve(x)
  }
}
  Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
  }
  Promise.resolve = function (value) {
    return new Promise(function (resolve, reject) {
      resolve(value)
    })
  }
  Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
      reject(reason)
    })
  }
  Promise.all = function (promises) {
    if (!Array.isArray(promises)) {
      throw new TypeError('必须传入promise数组');
    }
    var length = promises.length
    var values = []

    return new Promise(function (resolve, reject) {
      function rejectHandle(reason) {
        reject(reason) // 只要其中一个reject,整体reject
      }

      function resolveHandle(index) {
        return function (value) {
          values[index] = value // 按传入时的顺序记录每个promise的结果值
          if (--length === 0) { // 所有子promise都resolve后,整体resolve
            resolve(values)
          }
        }
      }
      
      promises.forEach(function (item, index) {
        item.then(resolveHandle(index), rejectHandle)
      })
    })
  }
  Promise.race = function (promises) {
    if (!Array.isArray(promises)) {
      throw new TypeError('必须传入promise数组');
    }
    return new Promise(function (resolve, reject) {
      function rejectHandle(reason) {
        reject(reason)
      }

      function resolveHandle(value) {
        resolve(value)
      }
      promises.forEach(function (item) {
        item.then(resolveHandle, rejectHandle)
      })
    })
  }
  // 不管resolved还是rejected,都会执行,避免同样的语句需要在then()和catch()中各写一次的情况。
  Promise.prototype.finally = function (callback) { 
    return this.then(callback, callback)
  }

测试:

使用promises-aplus-tests:全局安装npm i promises-aplus-tests -g,然后命令行 promises-aplus-tests [js文件名] 进行测试

注意:测试前要在尾部加上下面的代码:

Promise.deferred = Promise.defer = function () {
  let deferred = {};
  deferred.promise = new Promise(function (resolve, reject) {
      deferred.resolve = resolve;
      deferred.reject = reject;
  });
  return deferred
};

module.exports = Promise

测试完成后可删除

使用 UMD 规范封装:

(function (global, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(factory)
  } else if (typeof exports === 'object' && typeof module !== 'undefined') {
    // CommonJS (如node)
    module.exports = factory()
  } else {
    // 浏览器全局变量
    global.promisePolyfill = factory()
  }
})(this, function () {
  'use strict';
   /*
      定义Promise的代码
   */  

  function promisePolyfill () {
    var global = null
    try {
      global = Function('return this')();
    } catch (e) {
      throw new Error('全局对象不可用');
    }
    global.Promise = Promise
  }
  return promisePolyfill
})

使用

promisePolyfill()  // 注册Promise全局变量
posted @ 2018-06-22 18:09  mr_lao  阅读(544)  评论(0编辑  收藏  举报