js基础之Promise(全面+手写实现)

news2025/6/17 14:33:16

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状态变为已解决(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);
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/411908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(大数据开发随笔8)Hadoop 3.3.x分布式环境部署——补充知识

索引克隆虚拟机SSH免密登录ssh简介免密登录scp命令时间同步克隆虚拟机 克隆:注意要分开放置三个虚拟机的位置 修改克隆机的ip地址 vi /etc/sysconfig/network-scripts/ifcfg-ens33——IPADDR重启网络 systemctl restart networkip addr 查看ip地址 修改克隆机的主…

【STM32学习】直接存储器访问——DMA

【STM32学习】直接存储器访问——DMA零、参考一、对DMA的理解二、DMA通道优先级三、DMA通道x传输数量寄存器(DMA_CNDTRx)四、DMA缓冲区设计零、参考 一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制 【组件】通用环形缓冲区模块 上述是我的参考…

ServletContext 对象

1.共享数据 ServletContext 对象 先调用对象,获取对象,往里面存数据 package com.kuang.servlet;import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.…

Pandas.read_excel详解

文章目录基础知识语法参数详解-index_col参数详解-header参数详解-usecols参数详解-dtype其他参数多表读取顺带提一句如何用pandas写数据到excel基础知识 pandas 可以读取多种的数据格式,针对excel来说,可以使用read_excel()读取数据,如下&a…

实现在SpringBoot项目中使用腾讯云发送短信

在一些项目中总会不可避免的要用到短信服务,比如发送验证码等,那么如何进行短信的发送呢,我们这就来捋一捋,我这里采用的是腾讯云的短信服务。其他的云服务也大致一样。 第一步、申请腾讯云的短信服务并配置基本信息 首先进入腾讯…

2023年Q1京东大家电销售数据分析(京东行业大盘销量查询)

2023年第一季度,大家电线上市场的涨势有点放缓,相较于去年的涨幅,今年有收敛不少。下面,我们以京东平台的数据作为线上市场表现的参考。 根据鲸参谋数据显示,今年Q1季度大家电在京东的累计销量超过1600万件&#xff0c…

FE_CSS 页面布局之盒子模型 边框 内外边距

1 盒子模型(Box Model)组成 所谓 盒子模型:就是把 HTML 页面中的布局元素看作是一个矩形的盒子,也就是一个盛装内容的容器。 CSS 盒子模型本质上是一个盒子,封装周围的 HTML 元素,它包括:边框、…

用户体验设计之记忆(Memory)

🔥人类记忆的基础 记忆是能够记住过去的经历,以及回忆和唤起它们去影响当下的能力 记忆是由encoding(把外界刺激进入大脑转换成可以记忆的格式)、storage(在大脑中开辟存储记忆的空间)、retrieval&#x…

C语言文件操作复习回顾(1)

关于流,内存数据读写铺垫的复习 打开一个文件 -> 内存里面就会创建一个对应关联的文件信息区 -> 文件信息区其实就是一个FILE类型的结构体 -> 各个结构体成员记录了该文件的种种信息 -> 结构体(文件信息区)由FILE* 结构体指针来维护 -> 有了指针&am…

Linux -- 基础IO

文章目录1. 基础认识2. 回顾C文件接口2.1 现象一2.2 现象二2.3 fprintf()函数回顾2.4 fnprintf()函数使用2.5 "a"模式3. 系统接口3.1 open()和close()3.2 write()3.3 read()3.4 C文件接口和系统接口关系3.5 文件描述符3.6 深度理解Linux下一切皆文件3.7 FILE是什么3.…

OpenAI的ChatGPT迭代速度超乎想象

OpenAI的ChatGPT迭代速度超乎想象,被谷歌聊天机器人Bard 嘲笑“家里不通网”后,OpenAI一个鲤鱼打挺甩出“实时联网”方案。 美西时间3月23日,OpenAI宣布推出ChatGPT Plugins(插件集),赋予ChatGPT实时更新、…

chatGPT文章生成插件-用chatGPT写文章接入网站

chatGPT文章生成插件 ChatGPT是一种强大的自然语言生成技术,可以用于生成文章、文本摘要、对话生成、语言翻译等多种应用场景。在实际应用中,可以使用一些插件来实现ChatGPT的文章生成功能,以下是一些可能有用的插件介绍: AI Wri…

【CSS】实现梯形

简单说一下背景,就是需要实现一个展开收起的按钮。 那我第一个想到的方法,可不就是border自己画嘛。 1、border边框 width: 150px; border-top: 50px solid red; // 梯形的高 border-right: 50px solid transparent; // 往右延伸 border-bottom: 50px …

75-网络二(网络分层模型)

网络二二.网络分层模型1.网络协议(1)什么是网络协议(2)常见的协议2.OSI的7层模型与tcp/ip协议族体系4层结构(1)数据链路层(2)网络层(3)传输层(4)应用层3.为什么要分层?三.网络应用程序的通信流程网络一见:网络一 二.网络分层模型 1.网络协议 (1)什么是网络协议 网络协议就…

Python 小型项目大全 76~81

七十六、井字棋 原文:http://inventwithpython.com/bigbookpython/project76.html 井字棋是一种在3 3网格上玩的经典纸笔游戏。玩家轮流放置 X 或 O 标记,试图连续获得三个。大多数井字棋都以平局告终,但如果你的对手不小心,你也…

Three.js教程:顶点位置数据解析渲染

推荐:将NSDT场景编辑器加入你3D工具链其他工具系列:NSDT简石数字孪生顶点位置数据解析渲染 如果你没有WebGL基础,可以先不用记忆每个的threejs 具体内容,有一个大致印象即可,学习本节课的重点是建立顶点的概念。如果你…

Reactor设计模式

一、Reactor设计模式 1、什么是Reactor设计模式? Reactor模式是高性能I/O设计中,常用的设计模式。其中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上,一旦有I/O事件到来或是准备就绪…

C++ 继承:概念定义、对象的赋值转换、继承作用域及派生类的默认成员函数

目录 一. 继承的概念和定义 1.1 继承的概念 1.2 继承的定义格式 1.3 继承关系和访问限定符 二. 基类和派生类对象的之间的赋值转换 三. 继承体系中的作用域 四. 派生类的默认成员函数 4.1 构造函数 4.2 拷贝构造函数 4.3 赋值运算符重载函数 4.4 析构函数 4.5 取地…

linux服务器禁止ping命令,linux服务器禁ping如何解除

linux服务器禁止ping命令,linux服务器禁ping如何解除 我是艾西,在我们搭建网站或做某些程序时,不少人会问禁ping是什么意思,怎么操作的对于业务有哪些好处等,今天艾西一次给你们说清楚。 禁PING的意思是:不…

相对全面的四足机器人驱动规划MATLAB和Simulink实现方式(足端摆线规划,Hopf-CPG,Kimura-CPG)

许久没更新四足机器人相关的博客文章,由于去年一整年都在干各种各样的~活,终于把硕士毕业论文给写好,才有点时间更新自己的所学和感悟。步态规划和足端规划只是为了在运动学层面获取四足机器人各关节的期望角位移和速度信号,再由底…