手写promise

2022/8/12 javascriptpromisetypeScript

# 手撸符合 Promise/A+规范的 promise

Promise/A+原文 (opens new window) Promise/A+中译文 (opens new window)

interface INoopFunc {
  (p: any): void;
}

type QlPromiseFuncType = (resolve: INoopFunc, reject: INoopFunc) => void;

/**
 * 使用 class 来实现 Promise 核心功能
 */
export default class QlPromise {
  /**表示初始化时等待决议的状态 */
  static PENDING = "pending";
  /**表示决议之后的完成状态 */
  static FULFILLED = "fulfilled";
  /**表示决议之后的拒绝状态 */
  static REJECTED = "rejected";

  public PromiseState: string = "";
  public PromiseResult: any;
  /**保存完成状态的回调 */
  private onFulfilledCallbacks: Array<INoopFunc>;
  /**保存拒绝状态的回调 */
  private onRejectedCallbacks: Array<INoopFunc>;

  constructor(func: QlPromiseFuncType) {
    this.PromiseState = QlPromise.PENDING;
    this.PromiseResult = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    try {
      /**通过bind解决this指向问题 */
      func(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  resolve(result: any) {
    if (this.PromiseState === QlPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = QlPromise.FULFILLED;
        this.PromiseResult = result;
        this.onFulfilledCallbacks.map((callback) => callback(result));
      });
    }
  }

  reject(reason: any) {
    if (this.PromiseState === QlPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = QlPromise.REJECTED;
        this.PromiseResult = reason;
        this.onRejectedCallbacks.map((callback) => callback(reason));
      });
    }
  }

  then(onFulfilled?: INoopFunc, onRejected?: INoopFunc) {
    /**对参数断言确保并且确保是函数,这样能实现链式调用时值能穿透 */
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };

    /**
     * Promise/A+ 规范有说Promise.then的回调执行必须是异步的,实现方式是多种多样的,
     * 可以选择macro-task机制,也可以选择micro-task机制,
     * 我们上层手写时用的setTimeout就是macro-task机制。
     *
     * https://promisesaplus.com/#notes
     * https://www.ituring.com.cn/article/66566#note-1
     */
    const promise2 = new QlPromise((resolve, reject) => {
      const handleFulfilledCall = () => {
        setTimeout(() => {
          try {
            if (!onFulfilled) return;
            const x = onFulfilled(this.PromiseResult);
            resolvePromise({
              promise2,
              x,
              resolve,
              reject,
            });
          } catch (error) {
            reject(error);
          }
        });
      };
      const handleRejectedCall = () => {
        setTimeout(() => {
          if (!onRejected) return;
          try {
            const x = onRejected(this.PromiseResult);
            resolvePromise({
              promise2,
              x,
              resolve,
              reject,
            });
          } catch (error) {
            reject(error);
          }
        });
      };

      if (this.PromiseState === QlPromise.FULFILLED) {
        handleFulfilledCall();
      } else if (this.PromiseState === QlPromise.REJECTED) {
        handleRejectedCall();
      } else if (this.PromiseState === QlPromise.PENDING) {
        this.onFulfilledCallbacks.push(handleFulfilledCall);
        this.onRejectedCallbacks.push(handleRejectedCall);
      }
    });

    return promise2;
  }
}

type resolvePromiseParamType = {
  /**promise1.then方法返回的新的promise对象 */
  promise2: QlPromise;
  /**promise1中onFulfilled或onRejected的返回值 */
  x: any;
  /**promise2的resolve方法 */
  resolve: INoopFunc;
  /**promise2的reject方法 */
  reject: INoopFunc;
};
/**
 * 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
 */
function resolvePromise(opts: resolvePromiseParamType) {
  const { promise2, x, resolve, reject } = opts;
  /**避免循环引用报错 */
  if (x === promise2) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }
  // console.log('x',x)
  if (x && x instanceof QlPromise) {
    if (x.PromiseState === QlPromise.PENDING) {
      /**
       * 如何 x 处于等待决议的状态,promise 需要保持为 等待决议状态 直至 x 被执行或拒绝
       * 并且如果 x 调用了resolve 那得到的 y 也需要对 y 进行展开处理
       */
      x.then((y) => {
        resolvePromise({ promise2, x: y, resolve, reject });
      }, reject);
    } else if (x.PromiseState === QlPromise.FULFILLED) {
      /**
       * 如果 x 处于完成状态,用相同的值执行 promise
       */
      resolve(x.PromiseResult);
    } else if (x.PromiseState === QlPromise.REJECTED) {
      /**
       * 如果 x 处于拒绝状态,用相同的值拒绝 promise
       */
      reject(x.PromiseResult);
    } else if (
      x !== null &&
      (typeof x === "object" || typeof x === "function")
    ) {
      try {
        var then = x.then;
      } catch (error) {
        return reject(error);
      }

      /**
       * 如果 then 是函数,将 x 作为函数的作用域 this 调用之
       * 传递两个回调函数作为参数
       * 第一个参数叫做 `resolvePromise`, 第二个参数叫做 `rejectPromise`
       */
      if (typeof then === "function") {
        /**调用标识,避免多次调用 */
        let called = false;
        try {
          then.call(
            x,
            (y) => {
              if (called) return;
              called = true;
              resolvePromise({ promise2, x: y, resolve, reject });
            },
            (r) => {
              if (called) return;
              called = true;
              reject(r);
            }
          );
        } catch (error) {
          if (called) return;
          called = true;

          /**
           * 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
           */
          reject(error);
        }
      } else {
        // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
        resolve(x);
      }
    }
  } else {
    return resolve(x);
  }
}
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

# Promise 全部实例方法和静态方法的模拟实现

虽然这些都不在 Promise/A+ 规范里面,但是我们也来实现一下吧,加深理解。 其实我们前面我们用了很大功夫实现了 Promise/A+ ,实现这些已经是小菜一碟了,因为这些 API 全部是前面的封装而已。

阮一峰老师的 Es6/Promise 文档 (opens new window)

# 1.手撸 promise 全部实例方法

  • Promise.prototype.catch
class QlPromise {
  then(onFulfilled, onRejected) {...}
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}
1
2
3
4
5
6
  • Promise.prototype.finally

    在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。 这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。 由于无法知道 promise 的最终状态,所以 finally 的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。

根据规范我们这样实现:

class QlPromise {
  then(onFulfilled, onRejected) {...}
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
  finally(callback) {
    return this.then(callback, callback)
  }
}
1
2
3
4
5
6
7
8
9

# 2.手撸 promise 全部静态方法

  • Promise.resolve
class QlPromise {
  /**
   * QlPromise.resolve()
   * @param {[type]} value 要解析为 Promise 对象的值
   */
  static resolve(value) {
    if (value instanceof QlPromise) {
      return value;
    } else if (value instanceof Object && "then" in value) {
      // 如果是 thenable 的值需要先展开(鸭子类型)
      return new QlPromise((resolve, reject) => {
        value.then(resolve, reject);
      });
    }

    // 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
    return new QlPromise((resolve) => {
      resolve(value);
    });
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • Promise.reject
class QlPromise {
  /**
   * QlPromise.reject()
   * @param {any} reason 表示Promise被拒绝的原因
   */
  static reject(reason) {
    return new QlPromise((resolve, reject) => {
      reject(reason);
    });
  }
}
1
2
3
4
5
6
7
8
9
10
11
  • Promise.all

    Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个 Promise 实例, 输入的所有 promise 的 resolve 回调的结果是一个数组。

返回的这个 Promise 的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束, 或者输入的 iterable 里没有 promise 了的时候。 它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误, 并且 reject 的是第一个抛出的错误信息。

  • Promise.all 等待所有都完成(或第一个失败)
  • 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
  • 如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中,如果 promise 完成的话 (也就是如果参数里的某值不是 Promise,则需要原样返回在数组里)
  • 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。 如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成
class QlPromise {
  /**
   * QlPromise.all
   * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
   * @returns
   */
  static all(promises) {
    // TODO
  }
}
1
2
3
4
5
6
7
8
9
10
  • Promise.allSettled
  • Promise.race
  • Promise.any;

# Promise 核心总结

Last Updated: 2022/8/25 下午8:45:59