你能白瞟到的最“善解人意”的Promise实现

1,091 阅读17分钟

前沿

俗话说站在前辈巨擘的肩上,才能看的更远。在拜读了巨擘大咖们写的promise实现之后,全身经络通畅,如被先辈们用金手指点中了百会穴。至此以后,运行大小周天比往常更熟练了几分。然而,肩井穴,涌泉穴,每日亥时,都会隐隐作痛。思来想去,大概时先前巨擘的文章精妙,我一时无法全部领悟其中奥妙。固苦心感悟,方得此篇。不敢与前辈比肩,只当部分补充而已

直男不懂铺垫,所以直接干

这里我们用es6来实现,这样结构比较明显,如果有对es6不太熟悉的朋友,可以看看阮一峰老师的ECMAScript 6 入门

class myPromise {
            constructor(executor){
                this.value = null;
                this.reason = null;
                const resolve = value =>{
                    this.value = value;
                }
                const reject = reason => {
                    this.reason = reason;
                }
                try {
                    executor(resolve,reject)
                } catch (e) {
                    reject(e)
                }
            }
            then(onFulfilled,onRejected){
                    onFulfilled(this.value)
                    onRejected(this.reason)
            }
        }

代码解析:

  1. myPromise是一个构造函数,参数是一个高阶函数,接受两个内部函数resolve,reject
  2. myPromise的实例都可以调用then方法,then方法的第一个参数接受resolve的返回,第二个参数接受reject的返回

学武功的第一步是(自宫)对比

起手式

观察后发现:我们实现的myPormise和Promise运行的结果一致(good guy!),只是有一个小问题,我们是先调用的p1的then,但是p1的then返回却是在p2的then返回之后。我们保留疑问,继续淦!!!

resolve/reject的单一性

意思就是当一个promise中的resolve或者reject执行了之后,那之后resolve/reject都不再执行

观察之后,发现这就不对了

  1. 别人家的promise只返回了一个'p1-resolve',而咱们家的promise返回了'p2-resolve2'和'p2-reject1'
  2. 别家的promise和咱们家的promise不一样就算了,咋还少了一个'p2-resolve1'呢?这里的原因是reslove方法里面,我们用一个内部变量this.value保存了resolve的值,当执行resolve('p2-resolve1')之后,再执行resolve('p2-resolve2')时,this.value就被后者重新赋值了。然后再执行到then函数时,自然而然'p2-resolve1'就消失了。
  3. 怎么修改呢? 这里我们引入一个内部状态的概念来处理这个问题,初始状态我们设置为'pending',它只有一次改变状态的机会,它可以变成pending->fulfilled或者pending->rejected,一旦改变之后,就不在变化。(是不是像极了宝可梦的进化)。
  4. 另外为了保证resolve/reject执行的唯一性,当状态不在是pending时,resolve/reject就不执行

所以...(就决定是你了,皮卡丘!!!)

class myPromise {
            constructor(executor){
+               this.state = 'pending'; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                const resolve = value => {
+                   if(this.state === 'pending'){
+                       this.state = 'fulfilled';
                        this.value = value;
+                   }
                }
                const reject = reason => {
+                   if(this.state === 'pending'){
+                       this.state = 'rejected';
                        this.reason = reason;
+                   }
                }
                try {
                    executor(resolve,reject)
                } catch (e) {
                    reject(e)
                }
            }
            then(onFulfilled,onRejected){
+              if(this.state === 'fulfilled'){
                    onFulfilled(this.value)
+               }
+              if(this.state === 'rejected') {
                    onRejected(this.reason)
+              }
            }
        }

修改之后,再观察,咱们家的promise又正常了一些。

promise的异步执行

我们稍微清理一下我们的测试代码,使它看上去清爽自然

清理完之后。我们加上异步

通过观察,我们发现,我们的promise没有任何打印

  1. 仔细思考之后,我们可以想到,当resolve还没有执行的时候,then函数已经执行了,而这时的state为'pending',所以then函数里面不会有任何执行
  2. 我们怎么处理这种情况呢?首先可以想到,当resolve/reject的时候,这个时候执行then时的state为'pending',而这个时候,我们不知道之后到底时执行resolve呢?还是reject,所以我们只有将所有的then的所有的参数(onFulfilled/onRejected)都暂存起来,以待之后的state改变之后执行对应的方法(onFulfilled/onRejected)
  3. 当状态改变之后,立即执行暂存的对应的方法

基于上面所说,我们修改我们的promise

class myPromise {
            constructor(executor){
                this.state = 'pending'; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
+               this.onFulfilledCallbacks = [];
+               this.onRejectedCallbacks = [];
                const resolve = value =>{
                    if(this.state === 'pending'){
                        this.state = 'fulfilled';
                        this.value = value;
+                       this.onFulfilledCallbacks.forEach(fn=>fn(value))
                    }
                }
                const reject = reason => {
                    if(this.state === 'pending'){
                        this.state = 'rejected';
                        this.reason = reason;
+                       this.onRejectedCallbacks.forEach(fn=>fn(reason))
                    }
                }
                try {
                    executor(resolve,reject)
                } catch (e) {
                    reject(e)
                }
            }
            then(onFulfilled,onRejected){
                if(this.state === 'fulfilled'){
                    onFulfilled(this.value)
                }
                if(this.state === 'rejected') {
                    onRejected(this.reason)
                }
+               if(this.state === 'pending') {
+                   this.onFulfilledCallbacks.push(onFulfilled)
+                   this.onRejectedCallbacks.push(onRejected)
+                }
            }
        }

修改之后,我们看看结果 观察之后,发现我们的promise能够正常的显示then回调了,而且还有意外收获,仔细观察我们发现,p1.then和p2.then打印的顺序和调用时的顺序一致了!!!

同步异步一致性

不论promise里面的resolve/reject是同步或者异步,我们都希望then返回的执行表现一致 我们创建一个执行对比

经过观察,我们可以发现

  1. 别人家的promise,不论resolve是同步的还是异步的,then返回都是异步的
  2. 咱们家的promise,当resolve是异步的,then返回是异步的,当resolve是同步的返回就是同步的

那么怎么改呢?我们的目的是要保持then的执行一致,所以只能把全部的then修改成异步的,原因在上一节已经说明了 我们来修改一下咱们家的promise

class myPromise {
            constructor(executor) {
                this.state = 'pending'; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = value => {
                    if (this.state === 'pending') {
                        this.state = 'fulfilled';
                        this.value = value;
                        this.onFulfilledCallbacks.forEach(fn => fn(value))
                    }
                }
                const reject = reason => {
                    if (this.state === 'pending') {
                        this.state = 'rejected';
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach(fn => fn(reason))
                    }
                }
                try {
                    executor(resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }
            then(onFulfilled, onRejected) {
                if (this.state === 'fulfilled') {
+                   setTimeout(()=>{
                        onFulfilled(this.value)
+                   })
                }
                if (this.state === 'rejected') {
+                   setTimeout(()=>{
                        onRejected(this.reason)
+                   })
                }
                if (this.state === 'pending') {
+                   this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)}))
+                   this.onRejectedCallbacks.push((reason)=>setTimeout(()=>{onRejected(reason)}))
                }
            }
        }

然后来看看执行

现在咱们家的promise就更像别人家的了

为什么是数组

细心的小伙伴会发现this.onFulfilledCallbacks和this.onRejectedCallbacks是数组,在then方法里面,当state === 'pending'时,我们把onFulfilled和onRejected添加再数组进数组,而不是直接赋值给this.onFulfilledCallbacks/this.onRejectedCallbacks。会觉得这里直接赋值好像也没有什么问题。

我们修改一下我们promise直接用变量存储

class myPromise {
        constructor(executor) {
          this.state = "pending"; //内部状态为pending,resolve,reject
          this.value = null;
          this.reason = null;
          this.onFulfilledCallbacks = [];
          this.onRejectedCallbacks = [];
          const resolve = (value) => {
            if (this.state === "pending") {
              this.state = "fulfilled";
              this.value = value;
-             this.onFulfilledCallbacks.forEach((fn) => fn(value));
+             this.onFulfilledCallbacks(value);
            }
          };
          const reject = (reason) => {
            if (this.state === "pending") {
              this.state = "rejected";
              this.reason = reason;
              this.onRejectedCallbacks.forEach((fn) => fn(reason));
            }
          };
          try {
            executor(resolve, reject);
          } catch (e) {
            reject(e);
          }
        }
        then(onFulfilled, onRejected) {
          if (this.state === "fulfilled") {
            setTimeout(() => {
                onFulfilled(this.value)
            })
          }
          if (this.state === "rejected") {
            setTimeout(() => {
                onRejected(this.reason)
            })
          }
          if (this.state === "pending") {
-           this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)}));
+           this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)});
            this.onRejectedCallbacks.push(onRejected);
          }
        }
      }

经过观察我们发现:

  1. 别人家的promise,两个then回调的onFulfilled都正常执行了,咱们家的只执行了第二个then回调的
  2. 咱们家的promise的问题是,第二个then执行的时候,覆盖了第一个then的onFulfilledCallbacks赋值。所以这里只会执行第二个then回调的onFulfilled

我们把代码回滚到上一节,然后进入下一节

链式调用

then方法无论何时都返回一个promise实例

我们仔细观察一下

  1. 别人家的promise是执行了第一个then方法之后,又成功的执行了第二个then方法
  2. 咱们家的promise是执行了第一个then方法之后,第二个then方法就报错了
  3. 因为咱们家的promise的then方法没有返回一个promise对象

我们按着我们的分析来修改一下我们的promise

class myPromise {
        constructor(executor) {
          this.state = "pending"; //内部状态为pending,resolve,reject
          this.value = null;
          this.reason = null;
          this.onFulfilledCallbacks = [];
          this.onRejectedCallbacks = [];
          const resolve = (value) => {
            if (this.state === "pending") {
              this.state = "fulfilled";
              this.value = value;
              this.onFulfilledCallbacks.forEach((fn) => fn(value));
            }
          };
          const reject = (reason) => {
            if (this.state === "pending") {
              this.state = "rejected";
              this.reason = reason;
              this.onRejectedCallbacks.forEach((fn) => fn(reason));
            }
          };
          try {
            executor(resolve, reject);
          } catch (e) {
            reject(e);
          }
        }
        then(onFulfilled, onRejected) {
          if (this.state === "fulfilled") {
            setTimeout(() => {
                onFulfilled(this.value)
            })
          }
          if (this.state === "rejected") {
            setTimeout(() => {
                onRejected(this.reason)
            })
          }
          if (this.state === "pending") {
            this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)}));
            this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)});
          }
+         return new myPromise((resolve,reject)=>{
+             resolve()
+         })
        }
      }

修改完成之后,我们执行看看

经过观察我们可以发现

  1. p2-resolve-then2已经成功的打印出来了,只是和别人家的promise打印的位置不太对

第二个then的返回值问题

第二个then的onFulfilled的参数是第一个onFuifilled的return值

所以我们修改一下我们执行代码

这里有一个小tips,这里用了并的逻辑运算符,console.log()这个函数返回一个undefined,所以在前面取了非

花开两朵各表一枝,

我们观察发现

  1. 别人家的promise的第二个then能够正常的接收到第一个then传递过来的值
  2. 咱们家的promis却不能,其实也不奇怪,咱们家的promise在then里面返回的promise中的resolve方法中没有参数!!!

我们按着这个思路,来改改咱们家的promise

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
+                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
+                            try {
+                                let x = onFulfilled(this.value);
+                                resolve(x)
+                            } catch (error) {
+                                reject(error)
+                            }
                        })
                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
+                            try {
+                                let x = onRejected(this.reason);
+                                //这里暂时有点问题,我们先这么写,因为这里比较符合逻辑
+                                reject(x)
+                            } catch (error) {
+                                reject(error)
+                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
+                            try {
+                                let x = onFulfilled(value)
+                                resolve(x)
+                            } catch (error) {
+                                reject(error)
+                            }
                        });
                        this.onRejectedCallbacks.push(reason => {
+                            try {
+                                let x = onRejected(reason)
+                                //这里暂时有点问题,我们先这么写,因为这里比较符合逻辑
+                                reject(x)
+                            } catch (error) {
+                                reject(error)
+                            }
                        });
                    }
                })
                return promise2;
            }
        }

最后来看看结果

观察可以返现,咱们家的promise已经和别人家的promise已经越来越像了(牛头人警告!)

then函数的onRejected的返回会被下一个then函数的onFulfilled方法接受

我们来看看例子

仔细观察我们发现

  1. 别人家的promise的then函数链的执行是p1-reject-then1(第一个then的onRejected)=>p1-resolve-then2(第二个then的onFulfilled)
  2. 咱们家的promise的then函数链的执行是p2-reject-then1(第一个then的onRejected)=>p2-reject-then2(第二个then的onRejected)
  3. 这里的不同是promise的reject不需要传递(这是约定,不要为我为啥)

我画了一个图

所以我们来修改咱们家的promise

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
                            try {
                                let x = onFulfilled(this.value);
                                resolve(x)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
-                               //这里暂时有点问题,我们先这么写,因为这里比较符合逻辑
-                               reject(x)
+                               resolve(x)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
                            try {
                                let x = onFulfilled(value)
                                resolve(x)
                            } catch (error) {
                                reject(error)
                            }
                        });
                        this.onRejectedCallbacks.push(reason => {
                            try {
                                let x = onRejected(reason)
-                               //这里暂时有点问题,我们先这么写,因为这里比较符合逻辑
-                               reject(x)
+                               reslove(x)
                            } catch (error) {
                                reject(error)
                            }
                        });
                    }
                })
                return promise2;
            }
        }

修改完了,我们来看看结果

bingo!!! 咱们家的promise又像了别人家的promise几分

调整一下结构

大体上的 结构已经完成了,接下来我们要调整一下,方便接下来的对比

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
                            try {
                                let x = onFulfilled(this.value);
-                               reslove(x)
+                               resolvePromise(promise,x,resolve,reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
-                               reslove(x)
+                               resolvePromise(promise,x,resolve,reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
                            try {
                                let x = onFulfilled(value)
-                               reslove(x)
+                               resolvePromise(promise,x,resolve,reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                        this.onRejectedCallbacks.push(reason => {
                            try {
                                let x = onRejected(reason)
-                               reslove(x)
+                               resolvePromise(promise,x,resolve,reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                    }
                })
                return promise2;
            }
        }
+   function resolvePromise(promise2,x,resovle,reject) {
+       resolve(x)
+   }

我们创建了一个函数resolvePromise 专门来处理resolve相关的逻辑,因为这部分高度复用,所以我们把它单独抽离成一个函数,目前这个函数里面还是只执行了resolve(x)

当onFulfilled/onRejected的返回值是一个promise对象时

观察可以发现,

  1. 别人家的promise正常的将promise里面的resolve值传递给了第二个then方法
  2. 咱们家的promise,像一个铁憨憨一下,把promise实例当一个值返回了

这里咱们要弄懂,promise链式调用,具体的实现逻辑

解析:

  1. 一般情况下,我们调用resolve传递的是当前then(onFulfilled,onRejected)的onFulfilled和onRejected的返回
  2. 当onFulfilled和onRejected的返回是一个promise实例时,resolve传递的是当前这个实例的then(onFullfilled,onRejected)中的onFullfilled/onRejected的返回

根据我们的理解,来修改咱们家的promise

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
                            try {
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
                            try {
                                let x = onFulfilled(value)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                        this.onRejectedCallbacks.push(reason => {
                            try {
                                let x = onRejected(reason)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                    }
                })
                return promise2;
            }
        }
        function resolvePromise(promise2, x, resolve, reject) {
+           if (x instanceof myPromise) {
+               if (x.state === 'pending') {
+                   x.then((y) => {
+                       resolvePromise(promise2, y, resolve, reject)
+                   }, reject)
+               } else {
+                   x.then(resolve, reject)
+               }
+           } else {
                resolve(x)
+           }
        }

修改了之后,我们来看看

观察以后,发现咱们家的promise也越来越标准化了

特殊中的特殊情况

当resolve返回的是一个包含then函数的对象,或者函数时,要执行一次这个then函数

首先我们来看看对比

经过观察我们可以发现,

  1. 别人家的promise是成功的返回了,fn1的入参
  2. 而咱们家的promise却吧整个对象都返回了
  3. 别人家的promise,调用了fn1和fn2,但是只执行了排在前面的fn1,是因为fn1和fn2和resolve/reject一样都有单一执行原则
  4. 小问题,如果then里面的fn1/fn2返回的是一个promise对象呢?这个问题仔细想想?
  5. 结论就是,我们还要按照promise的标准再一次处理一下

我们按照我们的想法来完善咱们家的promise

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
                            try {
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
                            try {
                                let x = onFulfilled(value)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                        this.onRejectedCallbacks.push(reason => {
                            try {
                                let x = onRejected(reason)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        });
                    }
                })
                return promise2;
            }
        }
        function resolvePromise(promise2, x, resolve, reject) {
            if (x instanceof myPromise) {
                if (x.state === 'pending') {
                    x.then((y) => {
                        resolvePromise(promise2, y, resolve, reject)
                    }, reject)
                } else {
                    x.then(resolve, reject)
                }
+            } else if(x && (typeof x === 'object'|| typeof x === 'function')){
+                let called = false;
+                try {
+                    let then  = x.then;
+                    if(typeof then === 'function'){
+                        then.call(x,(y)=>{
+                            if(called) return;
+                            called = true;
+                            resolvePromise(promise2,y,resolve,reject);
+                        },(e)=>{
+                            if(called) return;
+                            called = true;
+                            reject(e)
+                        })
+                    }else{
+                        resolve(x)
+                    }
+                } catch (error) {
+                    if(called) return;
+                    called = true;
+                    reject(error)
+                }
+            }
            else {
                resolve(x)
            }
        }

修改完了之后,我们来看看结果

当然返回一个有then方法的函数结果也是一样的

可以看见咱们家的promise,又一次完成进化,现在快成长成一个独当一面的promise了

处理极端情况

then(onFulfilled,onRejected)中的onFulFilled返回的是当前的then()

我们来看看对比

观察可以发现

  1. 别人家的promise报错了
  2. 咱们家的promise没有反应
  3. 咱们家的promise没有反应,是因为它在无限的执行then,陷入了死循环

我们画个图

这是一个正常情况

当2这onFulfilled返回的是上面整个then时,就陷入了自我循环

所以这里,我们阻止一下自我循环

 class myPromise {
    ...
 }
 function resolvePromise(promise2, x, resolve, reject) {
+           if(promise2===x) {
+               return reject(new TypeError('自我循环'))
+           }
            if (x instanceof myPromise) {
            ...  
            }
    }

当then(onFulfilled,onRejected)中的onFulfilled,onRejected不为function时

这里是约定,所以没有啥理由

我们来加上

 then(onFulfilled, onRejected) {
+    onFulfilled = typeof onFulfilled === 'function'? onFulfilled:y=>y;
+    onRejected = typeof onRejected === 'function'? onRejected:y=>{throw y;}
      let promise2 = new myPromise((resolve, reject) => {

当一个promise的reslove的参数是一个promise的时候

暂时不知道为什么,有知道的大佬说明一下 这个也是一个约定,我们来看一个例子

观察之后,可以发现,resolve的promise传递是reslove的值,而reject是直接传递promise实例 所以我们来改造咱们家的promise

 const resolve = (value) => {
+                    if (value instanceof myPromise) {
+                        return value.then(resolve, reject)
+                    }
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };

到这里,咱们家的promise,大概就是这个样子的

class myPromise {
            constructor(executor) {
                this.state = "pending"; //内部状态为pending,resolve,reject
                this.value = null;
                this.reason = null;
                this.onFulfilledCallbacks = [];
                this.onRejectedCallbacks = [];
                const resolve = (value) => {
                    if (value instanceof myPromise) {
                        return value.then(resolve, reject)
                    }
                    if (this.state === "pending") {
                        this.state = "fulfilled";
                        this.value = value;
                        this.onFulfilledCallbacks.forEach((fn) => fn(value));
                    }
                };
                const reject = (reason) => {
                    if (this.state === "pending") {
                        this.state = "rejected";
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach((fn) => fn(reason));
                    }
                };
                try {
                    executor(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            then(onFulfilled, onRejected) {
                onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
                onRejected = typeof onRejected === 'function' ? onRejected : y => { throw y; }
                const promise2 = new myPromise((resolve, reject) => {
                    if (this.state === "fulfilled") {
                        setTimeout(() => {
                            // 万一onFulfilled方法报错,我们用trycatch捕获一下
                            try {
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })

                    }
                    if (this.state === "rejected") {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                    if (this.state === "pending") {
                        this.onFulfilledCallbacks.push(value => {
                            setTimeout(() => {
                                try {
                                    let x = onFulfilled(value)
                                    resolvePromise(promise2, x, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            })
                        });
                        this.onRejectedCallbacks.push(reason => {
                            setTimeout(() => {
                                try {
                                    let x = onRejected(reason)
                                    resolvePromise(promise2, x, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            })
                        });
                    }
                })
                return promise2;
            }
        }
        function resolvePromise(promise2, x, resolve, reject) {
            if (x === promise2) {
                return new TypeError('自我循环')
            }
            if (x instanceof myPromise) {
                if (x.state === 'pending') {
                    x.then((y) => {
                        resolvePromise(promise2, y, resolve, reject)
                    }, reject)
                } else {
                    x.then(resolve, reject)
                }
            } else if (x && (typeof x === 'object' || typeof x === 'function')) {
                let called = false;
                try {
                    let then = x.then;
                    if (typeof then === 'function') {
                        then.call(x, (y) => {
                            if (called) return;
                            called = true;
                            resolvePromise(promise2, y, resolve, reject);
                        }, (e) => {
                            if (called) return;
                            called = true;
                            reject(e)
                        })
                    } else {
                        resolve(x)
                    }
                } catch (error) {
                    if (called) return;
                    called = true;
                    reject(error)
                }
            }
            else {
                resolve(x)
            }
        }
    

最后用promises-aplus-tests测试一下我们的promise

npm init

npm i promises-aplus-tests -D

修改一下package.json

"scripts": {
    // 后面接你的文件
    "test": "promises-aplus-tests ./src/index.js"
  },

执行

npm run test

最后发现有40测试没通过

少年,你还记得一招从天而降的掌法吗?

这里,其他的文章会告诉这是promise的规范,就仿佛回到了小学时代,我懵懵懂懂的问老师问题时,老师告诉我只需要记住就行了。 但是,我拒绝!!!

我们来看一个极端的例子

为什么不一样的呢?

究其根本原因,resolvePromise返回了一个状态为fuifilled的promise

在promise内部的resolvePromise()方法内,直接执行了x.then(reslove,reject)

所以把custom thenable当成了值,错误的返回了

这里怎么处理呢

class myPromise {
    constructor(executor) {
        this.state = "pending"; //内部状态为pending,resolve,reject
        this.value = null;
        this.reason = null;
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (value instanceof myPromise) {
                return  value.then(resolve, reject);
            }
+            setTimeout(() => {
                if (this.state === "pending") {
                    this.state = "fulfilled";
                    this.value = value;
                    this.onFulfilledCallbacks.forEach((fn) => fn(value));
                }
+            })

        };
        const reject = (reason) => {
+            setTimeout(()=>{
                if (this.state === "pending") {
                    this.state = "rejected";
                    this.reason = reason;
                    this.onRejectedCallbacks.forEach((fn) => fn(reason));
                }
+            })
        };
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : y => { throw y; }
        const promise2 = new myPromise((resolve, reject) => {
            if (this.state === "fulfilled") {
                setTimeout(() => {
                    // 万一onFulfilled方法报错,我们用trycatch捕获一下
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })

            }
            if (this.state === "rejected") {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (this.state === "pending") {
                this.onFulfilledCallbacks.push(value => {
-                    setTimeout(() => {
                        try {
                            let x = onFulfilled(value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
-                    })
                });
                this.onRejectedCallbacks.push(reason => {
-                    setTimeout(() => {
                        try {
                            let x = onRejected(reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
-                    })
                });
            }
        })
        return promise2;
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('自我循环'))
    }
    if (x instanceof myPromise) {
        if (x.state === 'pending') {
            x.then((y) => {
                resolvePromise(promise2, y, resolve, reject)
            }, reject)
        } else {
            x.then(resolve, reject)
        }
    } else if (x && (typeof x === 'object' || typeof x === 'function')) {
        let called = false;
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, (e) => {
                    if (called) return;
                    called = true;
                    reject(e)
                })
            } else {
                resolve(x)
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error)
        }
    }
    else {
        resolve(x)
    }
}

我们把所有的resolve/reject都改成异步触发

最后的最后

我们执行 执行

npm run test

终于通过了全部的测试用例了

最终代码

class myPromise {
  constructor(executor) {
    this.state = "pending"; //内部状态为pending,resolve,reject
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (value instanceof myPromise) {
        return value.then(resolve, reject);
      }
      setTimeout(() => {
        if (this.state === "pending") {
          this.state = "fulfilled";
          this.value = value;
          this.onFulfilledCallbacks.forEach((fn) => fn(value));
        }
      });
    };
    const reject = (reason) => {
      setTimeout(() => {
        if (this.state === "pending") {
          this.state = "rejected";
          this.reason = reason;
          this.onRejectedCallbacks.forEach((fn) => fn(reason));
        }
      });
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (y) => y;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (y) => {
            throw y;
          };
    const promise2 = new myPromise((resolve, reject) => {
      if (this.state === "fulfilled") {
        setTimeout(() => {
          // 万一onFulfilled方法报错,我们用trycatch捕获一下
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
      if (this.state === "pending") {
        this.onFulfilledCallbacks.push((value) => {
          try {
            let x = onFulfilled(value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
        this.onRejectedCallbacks.push((reason) => {
          try {
            let x = onRejected(reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });
    return promise2;
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2) {
    return reject(new TypeError("自我循环"));
  }
  if (x instanceof myPromise) {
    if (x.state === "pending") {
      x.then((y) => {
        resolvePromise(promise2, y, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
  } else if (x && (typeof x === "object" || typeof x === "function")) {
    let called = false;
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (e) => {
            if (called) return;
            called = true;
            reject(e);
          }
        );
      } else {
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

myPromise.deferred = function () {
  let defer = {};
  defer.promise = new myPromise((resolve, reject) => {
    defer.resolve = resolve;
    defer.reject = reject;
  });
  return defer;
};

module.exports = myPromise;

本文使用 mdnice 排版