promiseA+规范的简单实现

实现 Promise A+ 规范

好像面试会考这个,于是开始尝试手写一波… 结果全是错,只能过 base 的测试用例,太真实了,还是去官网先复制一个研究下,我看了 Adehun 这个版本的,代码解耦的很棒,正好可以用来当模板练习。

1. Promise 基本使用

基本上 Promise 是有两种状态:确定态不确定态, 没有按照三状态来解释,明白意思就好!其中,状态一旦转为确定态就不可以改变了。

Promise 最常使用到的就是它的 then 方法,是注册异步回调的专有接口,它的好处有:回调函数不用写在参数的位置,使其更加声明式的挂载,还可以进行链式的 promise 调用。

2. Promise 的理解

写完整体 Promise A+ 代码后,感觉对 promise 又有了新的理解:

  1. 想要获取一个 promise 的结果必须新建一个 newPromise 去订阅它
  2. 基于上一步,promise 在发出通知的时候,必须保持异步,这样才能保证先订阅成功再统一触发通知
  3. 多个 promise 之间的订阅关系:上一个 promise 的回调是下一个 promise 状态的生产者

3. 设计思路

感觉先梳理个大体的轮廓再写比较好,不然写着写着就不知道下一步该写啥了。我对着 Adehun 这个版本的代码画个大体的流程:
在这里插入图片描述

4. 代码实现

ok, 接下来就顺着流程走就行了。

const enum State {
  PENDING = 0,
  FULFILLED = 1,
  REJECTED = 2,
}

const Utils = {
  isFunction: (x: any): x is Function => x && typeof x === 'function',
  isObject: (x: any): x is object => x && typeof x === 'object',
  isPromise: (x: any): x is MyPromise => x instanceof MyPromise,
  execAsync: (fn: Function) => setTimeout(fn),
};

const onFulfilledDefault = function (result: any) {
  return result;
};

const onRejectDefault = function (reason: any) {
  throw reason;
};

const Resolve = function (p: MyPromise, x: any) {
  if (p === x) {
    p.rejected(
      new TypeError("promise and it'value can't refer to same object")
    );
    return;
  }
  if (Utils.isPromise(x)) {
    if (x.state === State.PENDING) {
      x.then(
        function (y: any) {
          Resolve(p, y);
        },
        function (r: any) {
          p.rejected(r);
        }
      );
    } else {
      p.transition(x.state, x.value);
    }
    return;
  }
  if (Utils.isFunction(x) || Utils.isObject(x)) {
    let flag = false;
    try {
      const then = x.then;
      if (Utils.isFunction(then)) {
        then.call(
          x,
          function (y: any) {
            if (flag) return;
            flag = true;
            Resolve(p, y);
          },
          function (r: any) {
            if (flag) return;
            flag = true;
            p.rejected(r);
          }
        );
      } else {
        p.fulfilled(x);
      }
    } catch (e) {
      if (flag) return;
      flag = true;
      p.rejected(e);
    }
    return;
  }

  p.fulfilled(x);
};

type Handler = {
  resolve: null | Function;
  reject: null | Function;
};

class MyPromise {
  state: State = State.PENDING;
  value: any = undefined;
  queue: MyPromise[] = [];
  handler: Handler = {
    resolve: null,
    reject: null,
  };
  constructor(fn?: (resolve: Function, reject: Function) => void) {
    if (fn) {
      let flag = false;
      const that = this;
      fn(
        function (y: any) {
          if (flag) return;
          flag = true;
          Resolve(that, y);
        },
        function (r: any) {
          if (flag) return;
          flag = true;
          that.rejected(r);
        }
      );
    }
  }

  inform() {
    Utils.execAsync(() => {
      while (this.queue.length) {
        const p = this.queue.shift() as MyPromise;
        const handler =
          this.state === State.FULFILLED
            ? p.handler.resolve || onFulfilledDefault
            : p.handler.reject || onRejectDefault;

        try {
          const res = handler(this.value);
          Resolve(p, res);
        } catch (e) {
          p.rejected(e);
        }
      }
    });
  }

  then(onFulfilled: Function, onrejected: Function) {
    const p2 = new MyPromise();

    if (Utils.isFunction(onFulfilled)) {
      p2.handler.resolve = onFulfilled;
    }
    if (Utils.isFunction(onrejected)) {
      p2.handler.reject = onrejected;
    }

    this.queue.push(p2);
    if (this.state !== State.PENDING) {
      this.inform();
    }

    return p2;
  }

  transition(state: State, value: any) {
    if (this.state !== State.PENDING) return;

    this.state = state;
    this.value = value;

    this.inform();
  }

  rejected(reason: any) {
    this.transition(State.REJECTED, reason);
  }
  fulfilled(result: any) {
    this.transition(State.FULFILLED, result);
  }
}

// 测试
const adapter = {
  resolved(x: any) {
    return new MyPromise((resolve: any, reject: any) => {
      resolve(x);
    });
  },
  rejected(x: any) {
    return new MyPromise((resolve: any, reject: any) => {
      reject(x);
    });
  },
  deferred() {
    let resolve, reject;
    return {
      promise: new MyPromise((rev: any, rej: any) => {
        resolve = rev;
        reject = rej;
      }),
      resolve,
      reject,
    };
  },
};
const tests = require('promises-aplus-tests');

tests(adapter, (err: any) => {
  console.log(err);
});

5. 总结

一开始写的时候,promises-aplus-tests 报了很多的错,抓耳挠腮就是好一阵,总结下单词拼写可以使用 Code Spell Checker, 类型传参错误可以使用 AnyScript, 还有就是处理细节的错误,尤其是 Thenable 的处理很容易写漏逻辑,一开始还抱怨为什么还有同化异构的 promise 一说,写了几遍发现代码其实就加了一个 try-catchflag 的复杂度。

总的来说,写好一个完全通过 promises-aplus-tests 测试的代码还是挺有挑战的事,Adehun 这个版本没有使用一些高级的 js 语法,像函数式,bind, apply 等,用到的就是普通的函数和常规的流程控制语句,但是各个部分设计的都很合理,也契合规范。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值