从零实现Promise

技术文档网 2021-04-26
造轮子是为了更好的使用轮子

APromise遵循Promises/A+规范,拥有ES6.Promise的主要api,使用了setTimeout模拟异步机制。 实现过程可以看到 链式调用reject向下传播,及各个api 方法的意义。

代码实现


const identity = val => val;
const isFunction = func => typeof func === "function";
const State = {
  pending: "pending",
  resolved: "resolved",
  rejected: "rejected"
};

export default class APromise {
  constructor(fn) {
    // 初始化状态
    this.promiseState = State.pending;
    this.promiseValue = undefined;

    // 处理器数组
    this.hanlders = [];

    this.resolve = value => {
      return this.setResult(value, State.resolved, "resolve");
    };

    this.reject = reason => {
      return this.setResult(reason, State.rejected);
    };

    this.setResult = (value, state, fromWhere) => {
      const set = () => {
        if (this.promiseState !== State.pending) {
          return null;
        }

        if (value instanceof APromise && fromWhere === "resolve") {
          // 是否resolve取决于传入的promise
          return value.then(this.resolve, this.reject);
        }
        this.promiseState = state;
        this.promiseValue = value;
        return this.executeHandlers();
      };

      setTimeout(set);
    };

    this.executeHandlers = () => {
      if (this.promiseState === State.pending) {
        return null;
      }
      this.hanlders.forEach(handler => {
        if (this.promiseState === State.resolved) {
          handler.onResolved(this.promiseValue);
        } else {
          handler.onRejected(this.promiseValue);
        }
      });
      // 每次执行完毕后清空
      this.hanlders = [];
    };

    this.attachHandler = handler => {
      this.hanlders = [...this.hanlders, handler];
      this.executeHandlers();
    };

    try {
      fn(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  then(onResolved, onRejected) {
    onResolved = isFunction(onResolved) ? onResolved : identity;

    return new APromise((resolve, reject) => {
      this.attachHandler({
        onResolved: value => {
          try {
            resolve(onResolved(value));
          } catch (e) {
            reject(e);
          }
        },
        onRejected: reason => {
          try {
            isFunction(onRejected)
              ? resolve(onRejected(reason))
              : reject(reason);
          } catch (e) {
            reject(e);
          }
        }
      });
    });
  }

  catch(onRejected) {
    onRejected = isFunction(onRejected) ? onRejected : identity;
    return this.then(identity, onRejected);
  }
  finally(fn) {
    // finally保持promise最后的状态结果,与then不同
    return new APromise((resolve, reject) => {
      this.attachHandler({
        onResolved: value => {
          fn();
          resolve(value);
        },
        onRejected: reason => {
          fn();
          reject(reason);
        }
      });
    });
  }

  static resolve(v) {
    return new APromise((resolve, reject) => {
      return resolve(v);
    });
  }
  static reject(v) {
    return new APromise((resolve, reject) => {
      return reject(v);
    });
  }
  static all(arr) {
    return new APromise(function(resolve, reject) {
      var result = [],
        i = arr.length;
      arr.forEach(function(p, index) {
        p.then(
          function(data) {
            result[index] = data;
            i--;
            if (i === 0) {
              resolve(result);
            }
          },
          function(error) {
            reject(error);
          }
        );
      });
    });
  }
  static race(arr) {
    return new APromise(function(resolve, reject) {
      for (let p of arr.map(APromise.resolve)) {
        p.then(
          function(data) {
            resolve(data);
          },
          function(error) {
            reject(error);
          }
        );
      }
    });
  }
}

参考链接

相关文章

  1. webpack相关知识

    能够处理JS文件的互相依赖关系 能够处理JS的兼容问题 安装 全局安装 npm install webpack -g 项目安装 npm install webpack --save-

  2. Javascript基础和ES6

    HTML的基础事件 onmouseover、onmouseout表示JS事件的鼠标移入鼠标移出 document.getElementById('id') JS中选择HTML的ID元素<in

  3. JavaScript编码规则

    JavaScript编码规则 目的:改善协作编码、代码质量。 var 声明变量必须用var。 防止变量变为全局变量,污染全局环境。 常量 基本类型number、string、boolean是常量值。对

  4. JavaScript获取和设置CSS属性

    获取样式 元素对象的宽高位置距离等属性 如offsetWidht、cilentWidht、scrollWidth…… let oWidth=obj.offsetWidth; 注意: 只能获取属

  5. javascript作用域、上下文、this和闭包详解

    词法作用域lexical scope 定义在词法阶段的作用域。词法作用域是变量和语句块在定义时所处的位置决定的。 全局 块级:在{}之内是一个块级作用域(ES6之前没有块级作用于只有函数内的局部作用

随机推荐

  1. webpack相关知识

    能够处理JS文件的互相依赖关系 能够处理JS的兼容问题 安装 全局安装 npm install webpack -g 项目安装 npm install webpack --save-

  2. Javascript基础和ES6

    HTML的基础事件 onmouseover、onmouseout表示JS事件的鼠标移入鼠标移出 document.getElementById('id') JS中选择HTML的ID元素<in

  3. JavaScript编码规则

    JavaScript编码规则 目的:改善协作编码、代码质量。 var 声明变量必须用var。 防止变量变为全局变量,污染全局环境。 常量 基本类型number、string、boolean是常量值。对

  4. JavaScript获取和设置CSS属性

    获取样式 元素对象的宽高位置距离等属性 如offsetWidht、cilentWidht、scrollWidth…… let oWidth=obj.offsetWidth; 注意: 只能获取属

  5. javascript作用域、上下文、this和闭包详解

    词法作用域lexical scope 定义在词法阶段的作用域。词法作用域是变量和语句块在定义时所处的位置决定的。 全局 块级:在{}之内是一个块级作用域(ES6之前没有块级作用于只有函数内的局部作用