Skip to content

Promise笔记

作者:江月迟迟
发表于:2024-12-10
字数统计:6720 字
预计阅读23分钟

是什么

  • 语法上:Promise是由new Promise()构造函数构造得来的Promise对象。
  • 功能上:Promise是异步函数的解决方案

案例理解

javascript
const p1 = new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('fail')), 3000)
  })
  
  const p2 = new Promise(function (resolve, reject) {
    setTimeout(() => resolve(p1), 1000)
  })
  console.time('执行用时');
  p2
    .then(result => console.log('result is' + result))
    .catch(error => {
        console.log(error)
        console.timeEnd('执行用时');
    })

// Error: fail
//     at Timeout._onTimeout (c:\Users\HUAWEI\Desktop\index.js:27:29)
//     at listOnTimeout (node:internal/timers:569:17)
//     at process.processTimers (node:internal/timers:512:7)
// 执行用时: 3.025s

上面代码中,p1是一个 Promise,3 秒之后变为rejectedp2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

p2调用时,resolve(p1)函数立即执行,由于p2返回的是一个Promise,那么p2自己的状态无效,决定于p1的状态,然后看p1,p1立即执行reject()函数,reject函数将Error实例返回,等待3s后,p1状态变为rejected,p2的状态同样变为rejected,于是到catch里面。函数总共用了3s


javascript
const promise = new Promise((resolve, reject) => {
  // ... some code
  console.log("hello Promise");
  const RandNumber = Math.random() * 2;
  console.log(`Waiting ${RandNumber} seconds!`);
  setTimeout(() => {
    if (RandNumber >= 1) {
      resolve(`Resolve this value: ${RandNumber}, Success!`);
    } else {
      reject(`Reject this value: ${RandNumber}, Failed!`);
    }
  }, RandNumber * 1000);
});

promise.then(
  (value) => {
    // success
    console.log(value);
  },
  (error) => {
    // failure
    console.log(error);
  }
).catch(error => {
    console.log('我不执行');
    console.log(error);
});
js
 p2
    .then(result => console.log('result is' + result))
    .catch(error => {
        console.log(error)
        console.timeEnd('执行用时');
    })

对比两个实例,都是promise.then.catch链式调用,不过有所不同。

  • p2的.then调用返回的是Promise实例p1,.catch用于捕捉p1的reject。
  • promise不返回另一个promise,状态由自身决定,所以.catch作为.then的链式调用,捕捉.then方法的error,当then方法执行有误时,会捕捉成功。注意,then方法返回的是一个新的Promise(使得.then.then方法能够链式调用)
js
promise.then(
    (value) => {
      // success
      console.log(value);
      console.log(v);
    },
    (error) => {
      // failure
      console.log(error);
    }
  ).catch(error => {
      console.log('我会在then方法执行success的时候执行,因为v变量未声明');
      console.log(error);
  });

javascript
getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。


Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

javascript
getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});

上面代码中,一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

javascript
// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });

为什么

Promise用来处理异步操作,避免了回调地狱的出现。

怎么用

Promise.prototype.then()方法

javascript
getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
);

Promise.prototype.catch()

javascript
// 实际上是then()方法的特殊形式
p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

// 除此之外,catch方法可以捕获Promise方法,then()方法抛出的错误,而且catch方法遵守冒泡规则,可以捕获若干个之前的promise方法,then()方法抛出的错误
getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误:getJSON方法(其被处理成promise方法),以及两个.then方法
});

Promise.prototype.finally()

javascript
// 其不管promise是resolved还是rejected都会执行
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

Promise.all()

javascript
// 其接收一个数组,按顺序执行,如果如果p1, p2, p3的状态都变成fulfilled,p的状态才会变成fulfilled,当这个数组中有一个promise的状态变成rejected,p的状态立即变为rejected,并且立即执行.catch()方法
const p = Promise.all([p1, p2, p3]);

Promise.race()

js
// Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
// 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.allSettled()

js
// 前面的all()方法和race()方法行使了且、或的功能,但是还不够全面,且的方法有一个reject
// 时,就不关心其他的结果了,有没有一种方法,就是等到所有方法终止了才执行
const p = Promise.allSettled([p1, p2, p3]);
// 上面示例中,数组promises包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator()才会执行。

// 该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。

// 其当然能够监测每一个结果的状态:
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);

const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

Promise.any()

js
// Promise.race()方法获得一组异步操作方法中第一个结束的异步操作的结果,并作为新的Promise结果返回,有没有一种方法,是希望Promise成功的,只要有一个promise是fulfilled状态,返回fulfilled,只有当全部的promise都是rejected,才返回rejected
const p = Promise.any([p1, p2, p3]);

Promise.resolve()

js
// 1.如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

// 2.thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
// Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
// 上面代码中,thenable对象的then()方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then()方法指定的回调函数,输出42。

// 3. 如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。

// 4. Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

Promise.reject()

js
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了
//上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

// Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

Promise.reject('出错了')
.catch(e => {
  console.log(e === '出错了')
})
// true
// 上面代码中,Promise.reject()方法的参数是一个字符串,后面catch()方法的参数e就是这个字符串。

Promise.try()

js
Promise.try(() => database.users.get({id: userId}))
  .then(...)
  .catch(...)
// 事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

怎么实现

如何手写一个Promise?在ES6之前

跟着bili视频写了一遍,这是没有用class的版本

js
// const promise = new Promise((resolve, reject) => {
//   // ... some code
//   console.log("hello Promise");
//   const RandNumber = Math.random() * 2;
//   console.log(`Waiting ${RandNumber} seconds!`);
//   setTimeout(() => {
//     if (RandNumber >= 1) {
//       resolve(`Resolve this value: ${RandNumber}, Success!`);
//     } else {
//       reject(`Reject this value: ${RandNumber}, Failed!`);
//     }
//   }, RandNumber * 1000);
// });

// promise.then(
//     (value) => {
//       // success
//       console.log(value);
//       console.log(v);
//     },
//     (error) => {
//       // failure
//       console.log(error);
//       return new Error('捕捉失败的Error');
//     }
//   ).catch(error => {
//       console.log('我不执行');
//       console.log(error);
//   });

// const p1 = new Promise(function (resolve, reject) {
//     setTimeout(() => reject(new Error('fail')), 3000)
//   })

//   const p2 = new Promise(function (resolve, reject) {
//     setTimeout(() => resolve(p1), 1000)
//   })
//   console.time('执行用时');
//   p2
//     .then(result => console.log('result is' + result))
//     .catch(error => {
//         console.log(error)
//         console.timeEnd('执行用时');
//     })

// const promise = new Promise(function (resolve, reject) {
//     resolve('ok');
//     setTimeout(function () { throw new Error('test') }, 0)
//   });
//   promise.then(function (value) { console.log(value) });

// const resolved = Promise.resolve(42);
// const rejected = Promise.reject(-1);

// const allSettledPromise = Promise.allSettled([resolved, rejected]);

// allSettledPromise.then(function (results) {
//   console.log(results);
// });
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

// let thenable = {
//     then: function(resolve, reject) {
//       resolve(42);
//     }
//   };

//   let p1 = Promise.resolve(thenable);
// //   p1.then(function (value) {
// //     console.log(value);  // 42
// //   });

// 手写Promise

// // 1. 创建Promise实例化对象
// let p = new myPromise((resolve, reject) => {
//   console.log("this is a executor");
//   resolve(42);
//   // reject(666)
// });

// let p2 = p.then(
//   (value) => {
//     // 对应没有返回值START
//     console.log(`p.then()方法调用了!结果是value: ${value}`);
//     // 对应没有返回值END

//     // // 对应返回值为非promise对象 START
//     // return [1,2,3]
//     // // 对应返回值为非promise对象 END

//     // 对应返回值为error
//     // throw "errorere";

//     // 对应返回值为promise对象 START
//     // return new myPromise((resolve, reject) => {
//     //     // 返回promise-resolve
//     //     // resolve('okok')
//     //     // 返回promise-reject
//     //     // reject('nonono')
//     //     // 返回promise-error
//     //     // throw 'error'
//     // })
//     // 对应返回值为promise对象 END
//   },
//   (reason) => console.log(`p.then()方法调用了!结果是reason: ${reason}`)
// );
// console.log("同步执行的这一句应该显示在p.then()方法之前");

// setTimeout(() => {
//   console.log(p2);
// }, 1000);

// const p1 = new myPromise((resolve, reject) => {
//   // resolve(199)
//   // reject('nonononon')
//   // throw 'err'
//   // throw new Error('Err')

//   setTimeout(() => {
//     // console.log('我的网络很慢,发送这个请求需要3s,在此之前状态应该是pending');
//     // resolve(100);
//     reject('rrr')
//   }, 1000);

//   // resolve(100)
// });

// const p2 = p1.catch(reason => {
//     // console.log(`catch方法, ${reason}`);
//     // return reason
//     // throw new Error('fdjsalkfdasdsa')
// })

// console.log(p2);

// setTimeout(() => {
//     console.log(p2);
// }, 2000)

// new myPromise((resolve, reject) => {
//     resolve(1)
//     // reject('eororsre')
// }).then(value => {
//     throw '异常'
// }).then(value => {
//     console.log(value);
//     return 3
// }).then(value => {
//     console.log(value);
//     return 4
// }).then(value => {
//     console.log(value);
//     return 5
// }).catch(reason => {
//     console.log(reason);
// })

  // 15 promise.resolve方法
  myPromise.resolve = function(value) {
    if (value instanceof myPromise) {
        return value
    } else {
        return new myPromise(resolve => {
            resolve(value)
        })
    }
  }

  // 16. promise.reject方法,始终返回一个失败的promise方法
  myPromise.reject = function(value) {
    return new myPromise((undefined, reject) => {
        reject(value)
    })
  }

  // 17. promise.all方法,接收一个函数数组,按顺序执行这个函数数组,
  // 如果所有的都是fulfilled,返回一个promise,result是一个数组,内容是函数数组执行的结果
  // 如果有一个是rejected,直接返回promise, result是reject的值
  myPromise.all = function(promiseArr) {
    let result = new Array(promiseArr.length)
    let index = 0
    return new Promise((resolve, reject) => {
        promiseArr.forEach((item, i, arr) => {
            item.then(value => {
                index++
                // result.push(value)
                result[i] = value
                if(index === promiseArr.length) resolve(result)
            }, reason => {
                // 如果失败,直接拒绝
                reject(reason)
            })
        })
        // resolve(result)
    })
  }

  myPromise.race = function(promiseArr) {
    return new myPromise((resolve, reject) => {
        promiseArr.forEach(item => {
            item.then(value => {
                resolve(value)
            }, reason => {
                reject(reason)
            })
        })
    })
  }

  myPromise.allSettled = function(promiseArr) {
    let result = []
    let count = 0
    return new myPromise((resolve, reject) => {
        promiseArr.forEach(item => {
            item.then(value => {
                result.push({
                    status: 'fulfilled', value
                })
                count++
                if (count === promiseArr.length) {
                    resolve(result)
                }
            }, reason => {
                result.push({
                    status: 'rejected', reason
                })
                count++
                if (count === promiseArr.length) {
                    resolve(result)
                }
            })
        })
        
    })
  }

  myPromise.any = function(promiseArr) {
    let errors = []
    let count = 0
    return new myPromise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
            item.then(value => {
                resolve(value)
            }, reason => {
                errors[index] = reason
                count++

                // !不仅要在forEach里面检测,而且还要在处理错误的时候检测。在外面是同步的,无法等待结果
                if (count === promiseArr.length) {
                    reject(new AggregateError(errors, "All promises were rejected"))
                }
            })

        })

    })
  }

// const p1 = myPromise.resolve(1)
// console.log(p1);

// const p2 = myPromise.resolve(new myPromise((resolve, reject) => {
//     resolve(2)
// }))
// console.log(p2);

// const p3 = myPromise.resolve(new myPromise((resolve, reject) => {
//     reject(3)
// }))
// console.log(p3);

// const p1 = myPromise.reject(1)
// console.log(p1);

const p1 = new myPromise((resolve, reject) => {
    setTimeout(() => {
        reject(100)
    }, 100)
})

const p2 = new myPromise((resolve, reject) => {
    setTimeout(() => {
        reject(200)
    }, 75)
})

const p3 = new myPromise((resolve, reject) => {
    setTimeout(() => {
        reject(300)
    }, 150)
})

const pArr = myPromise.allSettled([p1, p2, p3])

setTimeout(() => {
    console.log(pArr);
}, 1000)
console.log(pArr);

// p1.then(
//   (value) => {
//     console.log(`成功1 ${value}`);
//   },
//   (reason) => {
//     console.log(`失败1 ${reason}`);
//   }
// );

// p1.then(
//   (value) => {
//     console.log(`成功2 ${value}`);
//   },
//   (reason) => {
//     console.log(`失败2 ${reason}`);
//   }
// );

// p1.then(
//   (value) => {
//     console.log(`成功3 ${value}`);
//   },
//   (reason) => {
//     console.log(`失败3 ${reason}`);
//   }
// );

// p1.then(
//   (value) => {
//     console.log(`成功4 ${value}`);
//   },
//   (reason) => {
//     console.log(`失败4 ${reason}`);
//   }
// );

// console.log(p1);

// setTimeout(() => {
//     console.log(p1);
// }, 4000)

// 2. 定义myPromise(),其得到一个函数,执行这个函数
function myPromise(executor) {
  // 准备部分:
  // 3.初始化
  this.PromiseResult = undefined;
  this.PromiseState = "pending";

  // 4. 保存this
  let _this = this;

  // 12. 保存之前pending状态的执行函数
  // 13. 兼容多个p1.then()方法,把待执行函数从{}变成数组
  this.previousCallbackFn = [];

  // 4.在myPromise里面,executor是() => {}这个函数,他接受两个参数,resolve, reject,实际上他们也是函数,所以需要定义和引用
  function resolve(value) {
    console.log(`hello, this is resolve, get a value: ${value}`);
    _this.PromiseResult = value;
    _this.PromiseState = "fulfilled";
  }

  // 5.如果用箭头函数定义,就可以不用this绑定
  const resolveArrowFn = (value) => {
    // 6.状态只能改变一次
    if (this.PromiseState !== "pending") return;
    // console.log(`hello, this is resolve, get a value: ${value}`);
    this.PromiseResult = value;
    this.PromiseState = "fulfilled";
    // // 12.2 异步执行的函数在这里执行,如果是pending,那么执行这个语句
    // this.previousCallbackFn.onfulfilled
    //   ? this.previousCallbackFn.onfulfilled()
    //   : "";

    // 13. 兼容多个p1.then() 方法(p1.then()的多次调用)
    this.previousCallbackFn.forEach(item => item.onfulfilled())
  };

  function reject(reason) {
    console.log(`oh no, this is reject, get a reason: ${reason}`);
    _this.PromiseResult = reason;
    _this.PromiseState = "rejected";
  }

  const rejectArrowFn = (reason) => {
    // 6.状态只能改变一次
    if (this.PromiseState !== "pending") return;
    // console.log(`oh no, this is reject, get a reason: ${reason}`);
    this.PromiseResult = reason;
    this.PromiseState = "rejected";
    // // 12.2 异步执行的函数在这里执行,如果是pending,那么执行这个语句
    // this.previousCallbackFn.onrejected
    //   ? this.previousCallbackFn.onrejected()
    //   : "";

    // 13. 兼容多个p1.then() 方法(p1.then()的多次调用)
    this.previousCallbackFn.forEach(item => item.onrejected())
  };

  // 7.then方法,放到promise.prototype.then()上
  // 11.当onfulfilled, onrejected没有传递函数来使用时,规定默认的函数,onfulfilled就是resolve(value),onrejected就是抛出错误
  
  //? 13.这里非常奇怪,只能用Object.assign合并对象,不能直接为myPromise.prototype添加then方法
  //! 13 问题已解决,因为之前用的是箭头函数,this指向混乱
  myPromise.prototype.then = function (onfulfilled = value => value, onrejected = reason => {throw reason}){
    // console.log(`实例化对象的状态是${this.PromiseState}`);
    // 9.then()方法对应几种情况:
    /**
     * then()方法返回一个新的promise实例
     * 1.返回一个值:p 以该返回值作为其兑现值。
     * 2.没有返回任何值:p 以 undefined 作为其兑现值。
     * 3.抛出一个错误:p 抛出的错误作为其拒绝值。
     * 4.返回一个已兑现的 Promise 对象:p 以该 Promise 的值作为其兑现值。
     * 5.返回一个已拒绝的 Promise 对象:p 以该 Promise 的值作为其拒绝值。
     * 6.返回另一个待定的 Promise 对象:p 保持待定状态,并在该 Promise 对象被兑现/拒绝后立即以该 Promise 的值作为其兑现/拒绝值。
     */

      return new myPromise((resolve, reject) => {
        // 10. 公共部分封装成函数
        const _commenFn = (callback) => {
          setTimeout(() => {
            try {
              const value = callback(this.PromiseResult);
              if (value instanceof myPromise) {
                value.then(resolve, reject);
              } else {
                resolve(value);
              }
            } catch (error) {
              reject(error);
            }
          });
        };

        if (this.PromiseState === "fulfilled") {
          // 8.不要立即执行,放到定时器里,可以在本轮事件循环的末尾执行
          _commenFn.call(this, onfulfilled);
        } else if (this.PromiseState === "rejected") {
          // 8.不要立即执行,放到定时器里,可以在本轮事件循环的末尾执行
          _commenFn.call(this, onrejected);
        } else {
          // 12.当promise中的代码也是异步时,this.PromiseState暂时为pending,
          // 上面的立即执行会失效,我们要等到状态变更为上述两个时再执行
          // console.log(`完蛋了,你想立即执行,可是我现在的状态是${this.PromiseState},执行了一个寂寞`);
          // 12.1 如果你想使用while等待,你会发现其实这里没有人处理这个状态,出现死循环
          // while(this.PromiseState === 'pending') {
          //     console.log('1');
          // }

          // if (this.PromiseState === "fulfilled") {
          //     // 8.不要立即执行,放到定时器里,可以在本轮事件循环的末尾执行
          //     _commenFn(onfulfilled)
          //   }
          //   else if (this.PromiseState === "rejected") {
          //     // 8.不要立即执行,放到定时器里,可以在本轮事件循环的末尾执行
          //     _commenFn(onrejected)
          //   }

          // 那么一种方法,可以出现这种情况时,把要执行的函数挂载到自己身上,重复调用自己,等待自己状态出现后,再执行原先的函数
          // 这种方法不能处理有多个p1.then的情况,previousCallbackFn应该兼容多个
        //   // 不兼容 START
        //   this.previousCallbackFn = {
        //     onfulfilled: _commenFn.bind(this, onfulfilled),
        //     onrejected: _commenFn.bind(this, onrejected),
        //   };
        //   // 不兼容 END

          // 兼容 START

          this.previousCallbackFn.push({
              onfulfilled: _commenFn.bind(this, onfulfilled),
              onrejected: _commenFn.bind(this, onrejected)
          })
          // 兼容 END
        }
      });
    
  }

  // 14 promise.catch方法
  myPromise.prototype.catch = function(onrejected) {
    return this.then(undefined, onrejected)
  }



  // 执行部分
  // 5. 在执行时,executor可能会发生错误,捕获异常信息使用trycatch
  try {
    executor(resolveArrowFn, rejectArrowFn);
  } catch (error) {
    _this.PromiseResult = typeof error === "object" ? error.message : error;
    _this.PromiseState = "rejected";
  }
}

// // 3. 如果在外部环境定义,为了不污染全局变量window,用这种方式:
// (function(window) {
//     function myPromiseExample(executor) {
//         executor()
//     }
//     window.myPromiseExample = myPromiseExample
// })(window)

ES6只用class的解决方案

js
// promise-es6.js

(function (window) {
  class Promise {
    constructor(executor) {
      // 初始化状态
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      // 回调函数的数组
      this.callbackFn = []

      // 使用箭头函数定义,this绑定在Promise里面
      const resolve = (value) => {
        // 状态只能改变一次
        if (this.PromiseState !== "pending") return;
        this.PromiseResult = value;
        this.PromiseState = "fulfilled";
        this.callbackFn.forEach(item => item.onfulfilled())
      };

      const reject = (reason) => {
        // 状态只能改变一次
        if (this.PromiseState !== "pending") return;
        this.PromiseResult = reason;
        this.PromiseState = "rejected";
        this.callbackFn.forEach(item => item.onrejected())

      };

      // 调用执行器函数
      try {
        executor(resolve, reject);
      } catch (error) {
        this.PromiseResult = typeof error === "object" ? error.message : error;
        this.PromiseState = "rejected";
      }
    }

    // 在原型上添加then方法
    then(onfulfilled = value => value, onrejected = reason => {throw reason}) {
      return new Promise((resolve, reject) => {

        // 封装公共函数
        const _commenFn = (callback) => {
          setTimeout(() => {
            // 获取不同回调函数的结果
            try {
              const value = callback(this.PromiseResult)
              if (value instanceof Promise) {
                value.then(resolve, reject)
              } else {
                resolve(value)
              }
            } catch (error) {
                reject(error)
            }
          })
        };

        if (this.PromiseState === 'fulfilled') {
          _commenFn(onfulfilled)
        } else if(this.PromiseState === 'rejected') {
          _commenFn(onrejected)
        } else {
          // 订阅
          this.callbackFn.push({
            onfulfilled: () => _commenFn(onfulfilled),
            onrejected: () => _commenFn(onrejected)
          })
        }
      })
    }

    // 在原型上添加catch方法
    catch(onrejected) {
      return this.then(undefined, onrejected)
    }

    // 在原型上添加finally方法
    finally(callback) {
      return this.then(
        value => Promise.resolve(callback()).then(() => value),
        reason => Promise.resolve(callback()).then(() => { throw reason })
      )
      
    }

    // 在类上添加resolve方法
    static resolve = function(value) {
      if (value instanceof Promise) {
        return value
      } else {
        return new Promise(resolve => {
          resolve(value)
        })    
      }
    }

    // 在类上添加reject方法
    static reject = function(value) {
      return new Promise((resolve, reject) => {
        reject(value)
      })
    }

    // 在类上添加all方法
    static all = function(promiseArr) {
      let result = []
      let count = 0
      return new Promise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
          // item有可能是普通值
          Promise.resolve(item).then(value => {
            result[index] = value
            count++

            // 在这里判断退出条件
            if (count === promiseArr.length) {
              resolve(result)
            }
          }, reason => {
            reject(reason)
          })
        })
      })
    }

    // 在类上添加race方法
    static race = function(promiseArr) {
      let result = []
      let count = 0
      return new Promise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
          Promise.resolve(item).then(value => {
            resolve(value)
          }, reason => {
            reject(reason)
          })
        })
      })
    }

    // 在类上添加allSettled方法
    static allSettled = function(promiseArr) {
      let result = []
      let count = 0
      return new Promise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
          Promise.resolve(item).then(value => {
            result[index] = {status: 'fulfilled', value}
            count++
            if (count === promiseArr.length) {
              resolve(result)
            }
          }, reason => {
            result[index] = {status: 'rejected', reason}
            count++
            if (count === promiseArr.length) {
              resolve(result)
            }
          })
        })
      })
    }

    //! 在类上添加allSettled方法 调用all版本:
    static allSettledByAll = function(promiseArr) {
      let result = [];
      let count = 0;
    
      // 使用 Promise.all 来等待所有 Promise 执行完成
      return Promise.all(promiseArr.map((item, index) => {
        // 对每个 Promise 对象进行处理,并返回一个新的 Promise
        return Promise.resolve(item).then(
          // 当 Promise 被 resolved 时,将结果存入 result 中
          value => {
            result[index] = { status: 'fulfilled', value };
          },
          // 当 Promise 被 rejected 时,将错误信息存入 result 中
          reason => {
            result[index] = { status: 'rejected', reason };
          }
        );
      })).then(() => {
        // 在所有 Promise 执行完成后,将结果返回
        return result;
      });
    };

    // any方法
    static any = function(promiseArr) {
      let errors = []
      let count = 0
      return new Promise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
          Promise.resolve(item).then(value => {
            resolve(value)
          }, reason => {
            errors[index] = reason
            count++
            if (count === promiseArr.length) {
              let myAggr = new AggregateError(errors, 'All promises were rejected');
              myAggr.stack = myAggr.stack.replace(/\n.*/g, '');
              reject(myAggr);
            }
          })
        })
      })
    }
    
  }
  window.Promise = Promise;
})(window);

Bug解决 \ 经验总结

finally的手写

finally本质上是then方法的特例。

javascript
promise
.finally(() => {
  // 语句
});

// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);

上面代码中,如果不使用finally方法,同样的语句需要为成功和失败两种情况各写一次。有了finally方法,则只需要写一次。

它的实现也很简单。

javascript
Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

上面代码中,不管前面的 Promise 是fulfilled还是rejected,都会执行回调函数callback

从上面的实现还可以看到,finally方法总是会返回原来的值。

javascript
// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

需要注意其他方法如catch方法会破坏原先的处理

js
const p5 = Promise.all([1, 2, 3, new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(666)
    }, 4000);
})]).catch(reason => {
    console.log(reason);
    return '现在我使用catch处理,原先的实例状态指向catch,状态应该是fulfilled'
});

console.log(p5);

image-20240423203555587

需要注意哪里是异步处理

参考资料

【【尚硅谷】Promise教程从入门到实战】

ECMAScript 6 入门

Promise-MDN