All files index.ts

100% Statements 75/75
92.3% Branches 12/13
100% Functions 32/32
100% Lines 62/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134    1x   1x 112x     112x 112x               112x 112x   71x 25x     3x 3x         71x 71x 71x       25x 25x 25x       71x 40x 2x 2x   38x 38x 36x 4x   32x     2x       71x 25x 7x 7x   18x 18x 17x 4x   13x     1x       71x     30x 30x 30x     25x   25x     16x   16x                 9x       4x   1x   3x         15x 12x   1x 3x 3x 3x 3x 1x   3x 3x 3x 3x 2x            
type Callback = (value?: any) => any;
 
const nextTick = globalThis.queueMicrotask || process.nextTick;
 
export class Promise {
  private state = "pending" as "pending" | "resolved" | "rejected";
  private value: any;
  private error: any;
  private pendThen: Callback[] = [];
  private pendCatch: Callback[] = [];
  static resolve: Callback;
  static reject: Callback;
  static all: (promises: Promise[]) => void;
 
  constructor(
    fn: (res: Promise["resolveValue"], rej: Promise["rejectValue"]) => void
  ) {
    try {
      fn(
        // TODO resolve promise
        (val) => this.resolveValue(val),
        (e) => this.rejectValue(e)
      );
    } catch (error) {
      this.state = "rejected";
      this.error = error;
    }
  }
 
  private resolveValue(val?: any) {
    this.state = "resolved";
    this.value = val;
    this.pendThen.forEach((fn) => fn(val));
  }
 
  private rejectValue(e?: any) {
    this.state = "rejected";
    this.error = e;
    this.pendCatch.forEach((fn) => fn(e));
  }
 
  then(resFn?: Callback, rejFn?: Callback): Promise {
    const baseResolve = (res: Callback, rej: Callback) => {
      if (!resFn) {
        res(this.value);
        return;
      }
      try {
        const resVal = resFn(this.value);
        if (typeof resVal?.then === "function") {
          resVal.then((v: any) => res(v));
        } else {
          res(resVal);
        }
      } catch (error) {
        rej(error);
      }
    };
 
    const baseReject = (res: Callback, rej: Callback) => {
      if (!rejFn) {
        rej(this.error);
        return;
      }
      try {
        const resVal = rejFn(this.error);
        if (typeof resVal?.then === "function") {
          resVal.then(res, rej);
        } else {
          res(resVal);
        }
      } catch (error) {
        rej(error);
      }
    };
 
    switch (this.state) {
      case "pending":
        // defer run after this promise resolved
        return new Promise((res, rej) => {
          this.pendThen.push(() => baseResolve(res, rej));
          this.pendCatch.push(() => baseReject(res, rej));
        });
      case "resolved":
        return new Promise((res, rej) =>
          // run at next tick
          nextTick(() => baseResolve(res, rej))
        );
      case "rejected":
        return new Promise((res, rej) =>
          // run at next tick
          nextTick(() => baseReject(res, rej))
        );
      /* istanbul ignore next */
      default:
        throw new Error("Unknown promise state: " + this.state);
    }
  }
 
  catch(rejFn: Callback) {
    return this.then(undefined, rejFn);
  }
 
  finally(fn: Callback) {
    return this.then(
      (v) => {
        Promise.resolve(fn()).then(() => v);
      },
      (v) => Promise.resolve(fn()).then(() => Promise.reject(v))
    );
  }
}
 
Promise.resolve = (val?: any) => new Promise((res) => res(val));
Promise.reject = (e?: any) => new Promise((_, rej) => rej(e));
 
Promise.all = (promises) => {
  const len = promises.length;
  let cnt = 0;
  return new Promise((res) => {
    if (!len) {
      res();
    }
    promises.forEach((promise) =>
      promise.then(() => {
        cnt++;
        if (cnt === len) {
          res();
        }
      })
    );
  });
};