一篇文章搞定 Promise(全面+手写实现)

lxf2023-05-13 01:01:06

我正在参加「编程·启航计划」

1. 是什么

Promise是一种异步编程的解决方案,用于处理异步操作并返回结果。 主要作用是解决回调函数嵌套(回调地狱)的问题,使异步操作更加清晰、易于理解和维护。

2. 怎么用

Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。当一个Promise被创建时,它的状态为pending。当异步操作完成并成功时,Promise的状态会变为fulfilled,并返回一个结果。当异步操作失败时,Promise的状态会变为rejected,并返回一个错误信息。

基本语法

// 创建一个promise
let promise = new Promise(function(resolve, reject) {
  // 异步操作
  if (异步操作成功) {
    resolve(value); // 将异步操作的结果传递给Promise对象,状态变为fulfilled
  } else {
    reject(error); // 将异步操作的错误信息传递给Promise对象,状态变为rejected
  }
});

promise.then(function(result) {
  // 异步操作成功时的处理代码
}).catch(function(error) {
  // 异步操作失败时的处理代码
});

常用方法

  1. then then中一般传入两个参数(函数),第一个对应resolve成功的回调,第二个对应reject失败的回调,如下

    
    function onResolved(res) {
    	console.log("resolved-" + res); // resolved-成功了
    }
    function onRejected(err) {
    	console.log("rejected-" + err); // rejected-失败了
    }
    
    new Promise((resolve, reject) => {
    	resolve('成功了');
    	// reject('失败了')
    }).then(onResolved, onRejected);
    

    then的第二个参数就等同于catch方法,但是需要注意

    • Promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,catch方法会捕捉到
    • catch不仅捕捉promise中抛出的错误,还会捕捉前面then中的错误
  2. catch 捕捉promise错误函数,和then的第二个参数(函数)作用一样,处理错误,由于Promise抛出错误具有冒泡性质,能够不断传递,会传到catch中,所以一般来说所有错误处理放在catch中,then中只处理成功的,同时catch还会捕捉then中第一个参数(函数)抛出的异常

    new Promise((resolve, reject) => {
    	reject();
    }).catch((err)=> {
    	console.log(err)
    });
    
  3. finally finally 方法没有参数,也不会改变 Promise 的状态,它只是在 Promise 结束时提供了一个通知机制,让我们可以在 Promise 结束后执行一些清理工作(比如操作文件的时候关闭文件流)。

    function onFinally() {
    	console.log('结束');
    }
    new Promise((resolve, reject) => {}).finally(onFinally);
    
  4. all 接受一个具有Iterable接口的类型,如数组,Map,Set,传入多个promise, 每一个promise执行成功resolve,最后才执行成功(返回一个Promise实例),进入then,否则失败进入catch

    function p1() {
      var promise1 = new Promise(function (resolve, reject) {
        console.log("p1的第一条输出语句");
        resolve("p1完成");
      });
      return promise1;
    }
    
    function p2() {
      var promise2 = new Promise(function (resolve, reject) {
        console.log("p2的第一条输出语句");
        setTimeout(() => {
          console.log("p2的第二条输出语句");
          resolve("p2完成");
        }, 2000);
      });
      return promise2;
    }
    
    function p3() {
      var promise3 = new Promise(function (resolve, reject) {
        console.log("p3的第一条输出语句");
        resolve("p3完成");
      });
      return promise3;
    }
    	Promise.all([p1(), p2(), p3()]).then(function (data) {
      console.log(data);
    });
    
    // 输出
    // p1的第一条输出语句;
    // p2的第一条输出语句;
    // p3的第一条输出语句;
    // p2的第二条输出语句[("p1完成", "p2完成", "p3完成")];
    
    var p1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 1000, 'one');
    });
    var p2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 2000, 'two');
    });
    var p3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 3000, 'three');
    });
    var p4 = new Promise((resolve, reject) => {
      setTimeout(resolve, 4000, 'four');
    });
    var p5 = new Promise((resolve, reject) => {
      reject('reject');
      // setTimeout(resolve, 5000, 'five');
    });
    Promise.all([p1, p2, p3, p4, p5]).then(values => {
      console.log(values); // [ 'one', 'two', 'three', 'four', 'five' ]
    }, reason => {
      console.log(reason) // reject
    });
    
  5. allSettled 接受一个具有Iterable接口的类型,如数组,Map,Set,传入多个promise, 每个promise状态改变成fulfilled或者rejected之后返回,返回的是一个数组对象,对象中有状态status和每一项的返回结果value

    Promise.allSettled([p1, p2, p3, p4, p5]).then(values => {
      console.log(values);
    }, reason => {
      console.log(reason)
    });
    // [
    //   { status: 'fulfilled', value: 'one' },
    //   { status: 'fulfilled', value: 'two' },
    //   { status: 'fulfilled', value: 'three' },
    //   { status: 'fulfilled', value: 'four' },
    //   { status: 'rejected', reason: 'reject' }
    // ]
    
  6. race 以快为准,数组中所有的promise对象,有一个先执行了何种状态,该对象就为何种状态,并执行相应函数 接受一个具有Iterable接口的类型,如数组,Map,Set,传入多个promise, 其中有一个promise返回了,不管是fulfilled或者rejected,直接返回这个promise的结果

    function p1() {
      var promise1 = new Promise(function (resolve, reject) {
        console.log("p1的第一条输出语句");
        resolve("p1完成");
      });
      return promise1;
    }
    
    function p2() {
      var promise2 = new Promise(function (resolve, reject) {
        console.log("p2的第一条输出语句");
        setTimeout(() => {
          console.log("p2的第二条输出语句");
          resolve("p2完成");
        }, 2000);
      });
      return promise2;
    }
    
    function p3() {
      var promise3 = new Promise(function (resolve, reject) {
        console.log("p3的第一条输出语句");
        resolve("p3完成");
      });
      return promise3;
    }
    
    Promise.race([p1(), p2(), p3()]).then(function (data) {
      console.log(data);
    });
    
    // 输出
    // p1的第一条输出语句
    // p2的第一条输出语句
    // p3的第一条输出语句
    // p1完成
    
  7. any 接受一个具有Iterable接口的类型,如数组,Map,Set,传入多个promise, 当传入的任何一个promise成功的时候,不管其他是否成功或者失败,会把成功的那个promise返回

如果传入的是一个空的数组(Map,Set),返回一个已失败状态的promise,如下 一篇文章搞定 Promise(全面+手写实现)

注意

Promise的以上方法都属于 微任务。当Promise状态变为已解决(resolved)或被拒绝(rejected)时,这些方法会产生微任务。这些微任务会在主线程空闲时按照顺序依次执行。

3. 有什么问题

  • 首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

4. 手写实现

手写实现有助于我们更好的理解Promise,开始吧!

我们使用Es6类的方式,构建一个Promise,更直观好理解

基本实现

  • 先写出一个雏形,维护三种状态,并定义reslove、reject、和then方法

    class MyPromise {
        static PENDING = "等待";
        static FULFILLED = "成功";
        static REJECTED = "失败";
        constructor(func) {
          this.status = MyPromise.PENDING; // 定义状态
          this.result = null; // 定义返回结果
          func(this.reslove.bind(this), this.reject.bind(this)); // 执行传入的函数
        }
        reslove(result) {
          this.status = MyPromise.FULFILLED; // 更改状态
          this.result = result;
        }
        reject(result) {
          this.status = MyPromise.FULFILLED; // 更改状态
          this.result = result;
        }
        then(onFULFILLED, onREJECTED) {
          if (this.status === MyPromise.FULFILLED) {
            // 执行成功后的回调
            onFULFILLED()
          }
          if (this.status === MyPromise.REJECTED) {
            // 执行失败后的回调
            onREJECTED()
          }
        }
    }
    
  • 注意

    当Promise中抛出错误时,会把promise的状态改为失败并且将错误设置为结果,如下

    const p = new Promise((resolve,reject)=> {
      throw new Error('出错')
    })
    p.then((res)=> {
      console.log(res)
    },(err)=> {
      console.log(err.message) // log: 出错
    })
    

    then中两个参数可以传入undefined

    const p = new Promise((resolve,reject)=> {
      resolve('成功')
    })
    p.then(undefined,(err)=> {
      console.log(err)
    })
    

    完善一下上面的两个问题,代码如下

    class MyPromise {
      static PENDING = "等待";
      static FULFILLED = "成功";
      static REJECTED = "失败";
      constructor(func) {
        this.status = MyPromise.PENDING; // 定义状态
        this.result = null; // 定义返回结果
        // 当Promise中抛出错误时,会把promise的状态改为失败并且将错误设置为结果
        try {
          func(this.reslove.bind(this), this.reject.bind(this)); // 执行传入的函数
        } catch (err) {
          this.reject(err);
        }
      }
      reslove(result) {
        this.status = MyPromise.FULFILLED; // 更改状态
        this.result = result;
      }
      reject(result) {
        this.status = MyPromise.FULFILLED; // 更改状态
        this.result = result;
      }
      then(onFULFILLED, onREJECTED) {
        // then中两个参数可以传入undefined,做下处理
        onFULFILLED =
          typeof onFULFILLED === "function"
            ? onFULFILLED
            : (value) => {
                value;
              };
        onREJECTED =
          typeof onREJECTED === "function"
            ? onREJECTED
            : (reason) => {
                throw reason;
              };
        if (this.status === MyPromise.FULFILLED) {
          // 执行成功后的回调
          onFULFILLED(this.result);
        }
        if (this.status === MyPromise.REJECTED) {
          // 执行失败后的回调
          onREJECTED(this.result);
        }
      }
    }
    
    
  • 继续完善 我们先写一个真实promises,看一下下面代码的执行顺序

    console.log('第一步')
    const p = new Promise((resolve,reject)=> {
      console.log('第二步')
      setTimeout(() => {
        resolve('成功')
        console.log('第四步')
      });
    })
    p.then((res)=> {
      console.log(res)
    },(err)=> {
      console.log(err)
    })
    console.log('第三步')
    // 依次输出如下:
    // 第一步
    // 第二步
    // 第三步
    // 第四步
    // 成功
    

    可以看到,then里面的代码是最后执行的,then属于微任务,resolve和reject 属于在事件循环末尾执行的,因此完善一下实现代码

    class MyPromise {
      static PENDING = "等待";
      static FULFILLED = "成功";
      static REJECTED = "失败";
      constructor(func) {
        this.status = MyPromise.PENDING; // 定义状态
        this.result = null; // 定义返回结果
        this.resloveCallbacks = []; // 存放reslove回调函数的数组
        this.rejectCallbacks = []; // 存放reject回调函数的数组
        // 当Promise中抛出错误时,会把promise的状态改为失败并且将错误设置为结果
        try {
          func(this.reslove.bind(this), this.reject.bind(this)); // 执行传入的函数
        } catch (err) {
          this.reject(err);
        }
      }
      reslove(result) { 
        // resolve是在事件循环末尾执行的,所以这里用setTimeout包裹一下
        setTimeout(() => {
          this.status = MyPromise.FULFILLED; // 更改状态
          this.result = result;
          // 遍历执行PENDING状态时保存的reslove回调函数
          this.resloveCallbacks.forEach((callback) => {
            callback(result);
          });
        });
      }
      reject(result) {
        // reject同样用setTimeout包裹一下
        setTimeout(() => {
          this.status = MyPromise.FULFILLED; // 更改状态
          this.result = result;
          // 遍历执行PENDING状态时保存的reject回调函数
          this.rejectCallbacks.forEach((callback) => {
            callback(result);
          });
        });
      }
      then(onFULFILLED, onREJECTED) {
        // then中两个参数可以传入undefined,做下处理
        onFULFILLED =
          typeof onFULFILLED === "function"
            ? onFULFILLED
            : (value) => {
                value;
              };
        onREJECTED =
          typeof onREJECTED === "function"
            ? onREJECTED
            : (reason) => {
                throw reason;
              };
        if (this.status === MyPromise.PENDING) {
          // PENDING状态时
          this.resloveCallbacks.push(onFULFILLED);
          this.rejectCallbacks.push(onREJECTED);
        }
        if (this.status === MyPromise.FULFILLED) {
        	setTimeout(() => {
          		// 执行成功后的回调
          		onFULFILLED(this.result);
          	})
        }
        if (this.status === MyPromise.REJECTED) {
        	setTimeout(() => {
          		// 执行失败后的回调
          		onREJECTED(this.result);
          	})
        }
      }
    }
    
    

    OK,到此我们可以测试一下

    console.log('第一步')
    const p = new MyPromise((resolve,reject)=> {
      console.log('第二步')
      setTimeout(() => {
        resolve('成功')
        console.log('第四步')
      });
    })
    p.then((res)=> {
      console.log(res)
    },(err)=> {
      console.log(err)
    })
    console.log('第三步')
    // 第一步
    // 第二步
    // 第三步
    // 第四步
    // 成功
    

实现then

  • 下面进入重头戏-----then的 链式调用 先看一下示例,如下

    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功");
      });
    });
    p.then((res) => {
      console.log("第一次then", res);
      return '返回一个正常值'
      // return new Promise((resolve)=> {
      //   setTimeout(() => {
      //     resolve('返回一个promise')
      //   });
      // })
      // throw "抛出一个异常"
    }).then(
      (res) => {
        console.log("第二次then", res);
      },
      (err) => {
        console.log("第二次then", err);
      }
    );
    // 第一次then 成功
    // 第二次then 返回一个正常值 | 第二次then 返回一个promise |第二次then 抛出一个异常
    

    如果 then 返回的是一个正常值,那么就会把这个结果(value)作为参数,传递给下一个 then 的成功的回调(onFULFILLED); 如果 then 中抛出了异常,那么就会把这个异常(reason)作为参数,传递给下一个 then 的失败的回调(onREJECTED); 如果 then 返回的是一个 promise 或者其他 thenable 对象,那么需要等这个 promise 执行完成,promise 如果成功,就走下一个 then 的成功回调;如果失败,就走下一个 then 的失败回调

    要实现上面功(链式调用),我们需要在then方法中,返回一个 promise

      then(onFULFILLED, onREJECTED) {
        let newPromise = new MyPromise((resolve, reject) => {
          // then中两个参数可以传入undefined,做下处理
          onFULFILLED =
            typeof onFULFILLED === "function"
              ? onFULFILLED
              : (value) => {
                  value;
                };
          onREJECTED =
            typeof onREJECTED === "function"
              ? onREJECTED
              : (reason) => {
                  throw reason;
                };
          if (this.status === MyPromise.PENDING) {
            // PENDING状态时
            this.resloveCallbacks.push(() => {
              try {
                let nextResult = onFULFILLED(this.result);
                resolve(nextResult)
              } catch (err) {
                reject(err);
              }
            });
            this.rejectCallbacks.push(() => {
              try {
                let nextResult = onREJECTED(this.result);
                resolve(nextResult)
              } catch (err) {
                reject(err);
              }
            });
          }
          if (this.status === MyPromise.FULFILLED) {
            setTimeout(() => {
              try {
                // 执行成功后的回调
                let nextResult = onFULFILLED(this.result);
                resolve(nextResult)
              } catch (err) {
                reject(err);
              }
            });
          }
          if (this.status === MyPromise.REJECTED) {
            setTimeout(() => {
              try {
                // 执行失败后的回调
                let nextResult = onREJECTED(this.result);
                resolve(nextResult)
              } catch (err) {
                reject(err);
              }
            });
          }
        });
        return newPromise;
      }
    

    这时我们测试一下,已经能实现链式调用了

    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功");
      });
    });
    p.then((res) => {
      console.log("第一次then", res);
      return "返回一个正常值";
    }).then(
      (res) => {
        console.log("第二次then", res);
      },
      (err) => {
        console.log("第二次then", err);
      }
    );
    // 第一次then 成功
    // 第二次then 返回一个正常值
    

    这样就完成了吗?并没有 上面我们的then执行回调的地方,我们获取到回调执行结果之后就直接resolve出去了,如下

    try {
      let nextResult = onFULFILLED(this.result);
      resolve(nextResult)
    } catch (err) {
      reject(err);
    }
    

    对于我们上面例子,直接返回一个基本数据类型("返回一个正常值")是可以实现的, 但是回调执行返回的结果,还可能存在其他类型,比如返回一个函数或者返回一个promise,或者失败抛出一个错误等等,对于这些情况,我们也需要进行处理

  • 完善then的链式调用-----处理then中返回 我们封装一个resolvePromise方法,用来处理不同情况

    then(onFULFILLED, onREJECTED) {
        let newPromise = new MyPromise((resolve, reject) => {
          // then中两个参数可以传入undefined,做下处理
          onFULFILLED =
            typeof onFULFILLED === "function"
              ? onFULFILLED
              : (value) => {
                  value;
                };
          onREJECTED =
            typeof onREJECTED === "function"
              ? onREJECTED
              : (reason) => {
                  throw reason;
                };
          if (this.status === MyPromise.PENDING) {
            // PENDING状态时
            this.resloveCallbacks.push(() => {
              try {
                let nextResult = onFULFILLED(this.result);
                resolvePromise(newPromise, nextResult, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
            this.rejectCallbacks.push(() => {
              try {
                let nextResult = onREJECTED(this.result);
                resolvePromise(newPromise, nextResult, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          }
          if (this.status === MyPromise.FULFILLED) {
            setTimeout(() => {
              try {
                // 执行成功后的回调
                let nextResult = onFULFILLED(this.result);
                resolvePromise(newPromise, nextResult, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          }
          if (this.status === MyPromise.REJECTED) {
            setTimeout(() => {
              try {
                // 执行失败后的回调
                let nextResult = onREJECTED(this.result);
                resolvePromise(newPromise, nextResult, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          }
        });
        return newPromise;
    }
    

    根据promise规范,实现的resolvePromise

    function resolvePromise(newPromise, x, resolve, reject) {
      if (x === newPromise) {
        // 因为x是回调的结果值,如果x指向newPromise即自己,那么会重新解析自己,导致循环调用
        throw new TypeError("禁止循环调用");
      }
      // 如果x是一个Promise,我们必须等它完成(失败或成功)后得到一个普通值时,才能继续执行。
      // 那我们把要执行的任务放在x.then()的成功回调和失败回调里面即可
      // 这就表示x完成后就会调用我们的代码。
    
      // 但是对于成功的情况,我们还需要再考虑下,x.then成功回调函数的参数,我们称为y
      // 那y也可能是一个thenable对象或者promise
      // 所以如果成功时,执行resolvePromise(promise2, y, resolve, reject)
      // 并且传入resolve, reject,当解析到普通值时就resolve出去,反之继续解析
      // 这样子用于保证最后resolve的结果一定是一个非promise类型的参数
    
      if (x instanceof MyPromise) {
        x.then(
          (y) => {
            resolvePromise(newPromise, y, resolve, reject);
          },
          (r) => reject(r)
        );
      }
      // (x instanceof myPromise) 处理了promise的情况,但是很多时候交互的promise可能不是原生的
      // 就像我们现在写的一个myPromise一样,这种有then方法的对象或函数我们称为thenable。
      // 因此我们需要处理thenable。
      else if ((typeof x === "object" || typeof x === "function") && x !== null) {
        // 暂存x这个对象或函数的then,x也可能没有then,那then就会得到一个undefined
        try {
          var then = x.then;
        } catch (e) {
          // 如果读取then的过程中出现异常则reject异常出去
          return reject(e);
        }
        // 判断then是否函数且存在,如果函数且存在那这个就是合理的thenable,我们要尝试去解析
        if (typeof then === "function") {
          // 状态只能更新一次使用一个called防止反复调用
          // 因为成功和失败的回调只能执行其中之一
          let called = false;
          try {
            then.call(
              x,
              (y) => {
                // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
                // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
                if (called) return;
                called = true;
                resolvePromise(newPromise, y, resolve, reject);
              },
              (r) => {
                // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
                // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
                if (called) return;
                called = true;
                reject(r);
              }
            );
            // 上面那一步等价于,即这里把thenable当作类似于promise的对象去执行then操作
            // x.then(
            //   (y) => {
            //     if (called) return;
            //     called = true;
            //     resolvePromise(newPromise, y, resolve, reject);
            //   },
            //   (r) => {
            //     if (called) return;
            //     called = true;
            //     reject(r);
            //   }
            // )
          } catch (e) {
            // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
            // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
            if (called) return;
            called = true;
            reject(e);
          }
        } else {
          // 如果是对象或函数但不是thenable(即没有正确的then属性)
          // 当成普通值则直接resolve出去
          resolve(x);
        }
      } else {
        return resolve(x);
      }
    }
    

    测试一下

    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功");
      });
    });
    p.then((res) => {
      console.log("第一次then", res);
      // return "返回一个正常值"; // 第二次then  返回一个正常值
      // return new MyPromise((resolve) => {
      //   setTimeout(() => {
      //     resolve("返回一个promise"); // 第二次then 返回一个promise
      //   });
      // });
      throw "抛出一个异常"; // 第二次then 抛出一个异常
    }).then(
      (res) => {
        console.log("第二次then", res);
      },
      (err) => {
        console.log("第二次then", err);
      }
    );
    

实现catch

  • 上面使用的时候介绍过,catch和then的第二个参数作用一样,所以catch的实现可以基于then

    catch(onREJECTED) {
      return this.then(null, onREJECTED);
    }
    

    测试一下,是没有问题的

    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功了");
        // reject('失败了')
      });
    });
    p.then((res) => {
      console.log("成功", res)
      throw '出错'
    }).catch((err) => {
      console.log("失败", err);
    });
    

实现finally

  • 当状态变化(等待变为成功或者失败)时都会进入finally,所以,依然可以借助then实现

    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(123);
      });
    });
    p.finally(() => {
      console.log('结束'); // 结束
    });
    

实现all

  • 该方法接收一个 Promise 数组(具有 Iterator 接口的对象),返回一个新的 Promise,该 Promise 在所有 Promise 都 resolve 后 resolve,如果其中有一个 Promise reject,则该 Promise 直接 reject。

    static all(promises) {
        return new MyPromise((resolve, reject) => {
          // 创建一个空数组results,用于存储每个Promise的 resolve 结果
          const results = [];
          let count = 0;
    
          const processResult = (index, result) => {
            // 结果存入results数组(具有Iterator接口的对象)中,并更新count变量
            results[index] = result;
            count++;
    
            if (count === promises.length) {
              // 如果count等于Promise 数组(具有Iterator接口的对象)的长度,则说明所有Promise都resolve了,此时调用 resolve 方法
              resolve(results);
            }
          };
          // 遍历传入的Promise数组(具有 Iterator 接口的对象),对每个Promise调用then方法
          for (const [index, promise] of [...promises].entries()) {
            MyPromise.resolve(promise).then((result) => {
              processResult(index, result);
            }, reject);
          }
        });
    }
    

    测试一下,没问题

    function p1() {
      var promise1 = new MyPromise(function (resolve, reject) {
        console.log("p1的第一条输出语句");
        resolve("p1完成");
      });
      return promise1;
    }
    
    function p2() {
      var promise2 = new MyPromise(function (resolve, reject) {
        console.log("p2的第一条输出语句");
        setTimeout(() => {
          console.log("p2的第二条输出语句");
          resolve("p2完成");
        }, 2000);
      });
      return promise2;
    }
    
    function p3() {
      var promise3 = new MyPromise(function (resolve, reject) {
        console.log("p3的第一条输出语句");
        resolve("p3完成");
      });
      return promise3;
    }
    MyPromise.all([p1(), p2(), p3()]).then(function (data) {
      console.log(data);
    });
    // 输出
    // p1的第一条输出语句;
    // p2的第一条输出语句;
    // p3的第一条输出语句;
    // p2的第二条输出语句[("p1完成", "p2完成", "p3完成")];
    
    var p1 = new MyPromise((resolve, reject) => {
      setTimeout(resolve, 1000, 'one');
    });
    var p2 = new MyPromise((resolve, reject) => {
      setTimeout(resolve, 2000, 'two');
    });
    var p3 = new MyPromise((resolve, reject) => {
      setTimeout(resolve, 3000, 'three');
    });
    var p4 = new MyPromise((resolve, reject) => {
      setTimeout(resolve, 4000, 'four');
    });
    var p5 = new MyPromise((resolve, reject) => {
      // reject('reject');
      setTimeout(resolve, 5000, 'five');
    });
    MyPromise.all([p1, p2, p3, p4, p5]).then(values => {
      console.log(values); // [ 'one', 'two', 'three', 'four', 'five' ]
    }, reason => {
      console.log(reason) // reject
    });
    // 5s后输出 [ 'one', 'two', 'three', 'four', 'five' ]
    

全部代码

class MyPromise {
  static PENDING = "等待";
  static FULFILLED = "成功";
  static REJECTED = "失败";
  constructor(func) {
    this.status = MyPromise.PENDING; // 定义状态
    this.result = null; // 定义返回结果
    this.resloveCallbacks = []; // 存放reslove回调函数的数组
    this.rejectCallbacks = []; // 存放reject回调函数的数组
    // 当Promise中抛出错误时,会把promise的状态改为失败并且将错误设置为结果
    try {
      func(this.reslove.bind(this), this.reject.bind(this)); // 执行传入的函数
    } catch (err) {
      this.reject(err);
    }
  }
  reslove(result) {
    // resolve是在事件循环末尾执行的,所以这里用setTimeout包裹一下
    setTimeout(() => {
      this.status = MyPromise.FULFILLED; // 更改状态
      this.result = result;
      // 遍历执行PENDING状态时保存的reslove回调函数
      this.resloveCallbacks.forEach((callback) => {
        callback(result);
      });
    });
  }
  reject(result) {
    // reject同样用setTimeout包裹一下
    setTimeout(() => {
      this.status = MyPromise.FULFILLED; // 更改状态
      this.result = result;
      // 遍历执行PENDING状态时保存的reject回调函数
      this.rejectCallbacks.forEach((callback) => {
        callback(result);
      });
    });
  }
  then(onFULFILLED, onREJECTED) {
    let newPromise = new MyPromise((resolve, reject) => {
      // then中两个参数可以传入undefined,做下处理
      onFULFILLED =
        typeof onFULFILLED === "function"
          ? onFULFILLED
          : (value) => {
              value;
            };
      onREJECTED =
        typeof onREJECTED === "function"
          ? onREJECTED
          : (reason) => {
              throw reason;
            };
      if (this.status === MyPromise.PENDING) {
        // PENDING状态时
        this.resloveCallbacks.push(() => {
          try {
            let nextResult = onFULFILLED(this.result);
            resolvePromise(newPromise, nextResult, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
        this.rejectCallbacks.push(() => {
          try {
            let nextResult = onREJECTED(this.result);
            resolvePromise(newPromise, nextResult, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      }
      if (this.status === MyPromise.FULFILLED) {
        setTimeout(() => {
          try {
            // 执行成功后的回调
            let nextResult = onFULFILLED(this.result);
            resolvePromise(newPromise, nextResult, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      }
      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          try {
            // 执行失败后的回调
            let nextResult = onREJECTED(this.result);
            resolvePromise(newPromise, nextResult, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      }
    });
    return newPromise;
  }
  catch(onREJECTED) {
    return this.then(null, onREJECTED);
  }
  finally() {
    return this.then(undefined, undefined);
  }
  static resolve(value) {
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 创建一个空数组results,用于存储每个Promise的 resolve 结果
      const results = [];
      let count = 0;

      const processResult = (index, result) => {
        // 结果存入results数组(具有Iterator接口的对象)中,并更新count变量
        results[index] = result;
        count++;

        if (count === promises.length) {
          // 如果count等于Promise 数组(具有Iterator接口的对象)的长度,则说明所有Promise都resolve了,此时调用 resolve 方法
          resolve(results);
        }
      };
      // 遍历传入的Promise数组(具有 Iterator 接口的对象),对每个Promise调用then方法
      for (const [index, promise] of [...promises].entries()) {
        Promise.resolve(promise).then((result) => {
          processResult(index, result);
        }, reject);
      }
    });
  }
}

function resolvePromise(newPromise, x, resolve, reject) {
  if (x === newPromise) {
    // 因为x是回调的结果值,如果x指向newPromise即自己,那么会重新解析自己,导致循环调用
    throw new TypeError("禁止循环调用");
  }
  // 如果x是一个Promise,我们必须等它完成(失败或成功)后得到一个普通值时,才能继续执行。
  // 那我们把要执行的任务放在x.then()的成功回调和失败回调里面即可
  // 这就表示x完成后就会调用我们的代码。

  // 但是对于成功的情况,我们还需要再考虑下,x.then成功回调函数的参数,我们称为y
  // 那y也可能是一个thenable对象或者promise
  // 所以如果成功时,执行resolvePromise(promise2, y, resolve, reject)
  // 并且传入resolve, reject,当解析到普通值时就resolve出去,反之继续解析
  // 这样子用于保证最后resolve的结果一定是一个非promise类型的参数

  if (x instanceof MyPromise) {
    x.then(
      (y) => {
        resolvePromise(newPromise, y, resolve, reject);
      },
      (r) => reject(r)
    );
  }
  // (x instanceof myPromise) 处理了promise的情况,但是很多时候交互的promise可能不是原生的
  // 就像我们现在写的一个myPromise一样,这种有then方法的对象或函数我们称为thenable。
  // 因此我们需要处理thenable。
  else if ((typeof x === "object" || typeof x === "function") && x !== null) {
    // 暂存x这个对象或函数的then,x也可能没有then,那then就会得到一个undefined
    try {
      var then = x.then;
    } catch (e) {
      // 如果读取then的过程中出现异常则reject异常出去
      return reject(e);
    }
    // 判断then是否函数且存在,如果函数且存在那这个就是合理的thenable,我们要尝试去解析
    if (typeof then === "function") {
      // 状态只能更新一次使用一个called防止反复调用
      // 因为成功和失败的回调只能执行其中之一
      let called = false;
      try {
        then.call(
          x,
          (y) => {
            // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
            // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
            if (called) return;
            called = true;
            resolvePromise(newPromise, y, resolve, reject);
          },
          (r) => {
            // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
            // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
            if (called) return;
            called = true;
            reject(r);
          }
        );
        // 上面那一步等价于,即这里把thenable当作类似于promise的对象去执行then操作
        // x.then(
        //   (y) => {
        //     if (called) return;
        //     called = true;
        //     resolvePromise(newPromise, y, resolve, reject);
        //   },
        //   (r) => {
        //     if (called) return;
        //     called = true;
        //     reject(r);
        //   }
        // )
      } catch (e) {
        // called就是用于防止成功和失败被同时执行,因为这个是thenable,不是promise
        // 需要做限制如果newPromise已经成功或失败了,则不会再处理了
        if (called) return;
        called = true;
        reject(e);
      }
    } else {
      // 如果是对象或函数但不是thenable(即没有正确的then属性)
      // 当成普通值则直接resolve出去
      resolve(x);
    }
  } else {
    return resolve(x);
  }
}
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!