手写Promise,理解内部原理

2023-01-11 20:39:31 浏览数 (2)

代码语言:javascript复制
1class myPromise {
2        // 为了统一用static创建静态属性,用来管理状态
3        static PENDING = "pending";
4        static FULFILLED = "fulfilled";
5        static REJECTED = "rejected";
6
7        // 构造函数:通过new命令生成对象实例时,自动调用类的构造函数
8        constructor(func) {
9          // 给类的构造方法constructor添加一个参数func
10          this.PromiseState = myPromise.PENDING; // 指定Promise对象的状态属性 PromiseState,初始值为pending
11          this.PromiseResult = null; // 指定Promise对象的结果 PromiseResult
12          this.onFulfilledCallbacks = []; // 保存成功回调
13          this.onRejectedCallbacks = []; // 保存失败回调
14          try {
15            /**
16             * func()传入resolve和reject,
17             * resolve()和reject()方法在外部调用,这里需要用bind修正一下this指向
18             * new 对象实例时,自动执行func()
19             */
20            func(this.resolve.bind(this), this.reject.bind(this));
21          } catch (error) {
22            // 生成实例时(执行resolve和reject),如果报错,就把错误信息传入给reject()方法,并且直接执行reject()方法
23            this.reject(error);
24          }
25        }
26
27        resolve(result) {
28          // result为成功态时接收的终值
29          // 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject)
30          if (this.PromiseState === myPromise.PENDING) {
31            /**
32             * 为什么resolve和reject要加setTimeout?
33             * 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
34             * 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
35             * 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。
36             */
37            setTimeout(() => {
38              this.PromiseState = myPromise.FULFILLED;
39              this.PromiseResult = result;
40              /**
41               * 在执行resolve或者reject的时候,遍历自身的callbacks数组,
42               * 看看数组里面有没有then那边 保留 过来的 待执行函数,
43               * 然后逐个执行数组里面的函数,执行的时候会传入相应的参数
44               */
45              this.onFulfilledCallbacks.forEach((callback) => {
46                callback(result);
47              });
48            });
49          }
50        }
51
52        reject(reason) {
53          // reason为拒绝态时接收的终值
54          // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
55          if (this.PromiseState === myPromise.PENDING) {
56            setTimeout(() => {
57              this.PromiseState = myPromise.REJECTED;
58              this.PromiseResult = reason;
59              this.onRejectedCallbacks.forEach((callback) => {
60                callback(reason);
61              });
62            });
63          }
64        }
65
66        /**
67         * [注册fulfilled状态/rejected状态对应的回调函数]
68         * @param {function} onFulfilled  fulfilled状态时 执行的函数
69         * @param {function} onRejected  rejected状态时 执行的函数
70         * @returns {function} newPromsie  返回一个新的promise对象
71         */
72        then(onFulfilled, onRejected) {
73          /**
74           * 参数校验:Promise规定then方法里面的两个参数如果不是函数的话就要被忽略
75           * 所谓“忽略”并不是什么都不干,
76           * 对于onFulfilled来说“忽略”就是将value原封不动的返回,
77           * 对于onRejected来说就是返回reason,
78           *     onRejected因为是错误分支,我们返回reason应该throw一个Error
79           */
80          onFulfilled =
81            typeof onFulfilled === "function" ? onFulfilled : (value) => value;
82          onRejected =
83            typeof onRejected === "function"
84              ? onRejected
85              : (reason) => {
86                  throw reason;
87                };
88
89          // 2.2.7规范 then 方法必须返回一个 promise 对象
90          let promise2 = new myPromise((resolve, reject) => {
91            if (this.PromiseState === myPromise.FULFILLED) {
92              /**
93               * 为什么这里要加定时器setTimeout?
94               * 2.2.4规范 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
95               * 这里的平台代码指的是引擎、环境以及 promise 的实施代码。
96               * 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
97               * 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。
98               */
99              setTimeout(() => {
100                try {
101                  // 2.2.7.1规范 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x),即运行resolvePromise()
102                  let x = onFulfilled(this.PromiseResult);
103                  resolvePromise(promise2, x, resolve, reject);
104                } catch (e) {
105                  // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
106                  reject(e); // 捕获前面onFulfilled中抛出的异常
107                }
108              });
109            } else if (this.PromiseState === myPromise.REJECTED) {
110              setTimeout(() => {
111                try {
112                  let x = onRejected(this.PromiseResult);
113                  resolvePromise(promise2, x, resolve, reject);
114                } catch (e) {
115                  reject(e);
116                }
117              });
118            } else if (this.PromiseState === myPromise.PENDING) {
119              // pending 状态保存的 resolve() 和 reject() 回调也要符合 2.2.7.1 和 2.2.7.2 规范
120              this.onFulfilledCallbacks.push(() => {
121                setTimeout(() => {
122                  try {
123                    let x = onFulfilled(this.PromiseResult);
124                    resolvePromise(promise2, x, resolve, reject);
125                  } catch (e) {
126                    reject(e);
127                  }
128                });
129              });
130              this.onRejectedCallbacks.push(() => {
131                setTimeout(() => {
132                  try {
133                    let x = onRejected(this.PromiseResult);
134                    resolvePromise(promise2, x, resolve, reject);
135                  } catch (e) {
136                    reject(e);
137                  }
138                });
139              });
140            }
141          });
142
143          return promise2;
144        }
145      }
146
147      /**
148       * 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
149       * @param  {promise} promise2 promise1.then方法返回的新的promise对象
150       * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
151       * @param  {[type]} resolve   promise2的resolve方法
152       * @param  {[type]} reject    promise2的reject方法
153       */
154      function resolvePromise(promise2, x, resolve, reject) {
155        // 2.3.1规范 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
156        if (x === promise2) {
157          return reject(new TypeError("Chaining cycle detected for promise"));
158        }
159
160        // 2.3.2规范 如果 x 为 Promise ,则使 promise2 接受 x 的状态
161        if (x instanceof myPromise) {
162          if (x.PromiseState === myPromise.PENDING) {
163            /**
164             * 2.3.2.1 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
165             *         注意"直至 x 被执行或拒绝"这句话,
166             *         这句话的意思是:x 被执行x,如果执行的时候拿到一个y,还要继续解析y
167             */
168            x.then((y) => {
169              resolvePromise(promise2, y, resolve, reject);
170            }, reject);
171          } else if (x.PromiseState === myPromise.FULFILLED) {
172            // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
173            resolve(x.PromiseResult);
174          } else if (x.PromiseState === myPromise.REJECTED) {
175            // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
176            reject(x.PromiseResult);
177          }
178        } else if (
179          x !== null &&
180          (typeof x === "object" || typeof x === "function")
181        ) {
182          // 2.3.3 如果 x 为对象或函数
183          try {
184            // 2.3.3.1 把 x.then 赋值给 then
185            var then = x.then;
186          } catch (e) {
187            // 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
188            return reject(e);
189          }
190
191          /**
192           * 2.3.3.3
193           * 如果 then 是函数,将 x 作为函数的作用域 this 调用之。
194           * 传递两个回调函数作为参数,
195           * 第一个参数叫做 `resolvePromise` ,第二个参数叫做 `rejectPromise`
196           */
197          if (typeof then === "function") {
198            // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
199            let called = false; // 避免多次调用
200            try {
201              then.call(
202                x,
203                // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
204                (y) => {
205                  if (called) return;
206                  called = true;
207                  resolvePromise(promise2, y, resolve, reject);
208                },
209                // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
210                (r) => {
211                  if (called) return;
212                  called = true;
213                  reject(r);
214                }
215              );
216            } catch (e) {
217              /**
218               * 2.3.3.3.4 如果调用 then 方法抛出了异常 e
219               * 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
220               */
221              if (called) return;
222              called = true;
223
224              /**
225               * 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
226               */
227              reject(e);
228            }
229          } else {
230            // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
231            resolve(x);
232          }
233        } else {
234          // 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
235          return resolve(x);
236        }
237      }
238
239      // 测试
240      const pro = new myPromise((resolve) => {
241        setTimeout(() => {
242          resolve("999");
243        }, 2000);
244      });
245
246      pro
247        .then((res) => {
248          console.log(res);
249          return res;
250        })
251        .then((res) => {
252          console.log(res);
253        });
254      //999
255      //999

0 人点赞