遵循Promises/A+规范实现自定义Promise(期约)

2022 年 8 月 22 日 星期一
/
23

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

遵循Promises/A+规范实现自定义Promise(期约)

该Promise类实现遵循Promises/A+规范,实现了thencatchfinally的链式调用以及allallSettledraceany 静态方法。

实现

class $Promise$ {
  constructor(executor) {
    // 初始化状态为pending
    this.status = 'pending';
    // 初始化成功的值为undefined
    this.value = undefined;
    // 初始化失败的原因为undefined
    this.reason = undefined;
    // 初始化成功处理函数队列
    this.onFulfilledCallbacks = [];
    // 初始化失败处理函数队列
    this.onRejectedCallbacks = [];

    // 定义resolve方法
    const resolve = (value) => {
      // 只有在pending状态才能更改状态和值
      if (this.status === 'pending') {
        // 如果是一个promise或者是一个类promise就在其then方法中调用
        // if(value instanceof $Promise$ || (value && typeof value.then === 'function')) {
        //   value.then(resolve, reject);
        //   return;
        // }
        this.status = 'fulfilled';
        this.value = value;
        // 执行所有成功处理函数
        this.onFulfilledCallbacks.forEach((callback) => callback());
      }
    };

    // 定义reject方法
    const reject = (reason) => {
      // 只有在pending状态才能更改状态和原因
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        // 执行所有失败处理函数
        this.onRejectedCallbacks.forEach((callback) => callback());
      }
    };

    // 立即执行执行器函数
    try {
      executor(resolve, reject);
    } catch (error) {
      // 如果执行器函数抛出异常,将Promise状态更改为rejected
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    // 如果不传处理函数,则使用默认处理函数
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason;
          };

    // 创建一个新的Promise实例,称为promise2
    const promise2 = new $Promise$((resolve, reject) => {
      if (this.status === 'fulfilled') {
        // 使用queueMicrotask保证异步调用
        queueMicrotask(() => {
          try {
            // 调用onFulfilled,并获取返回值
            const x = onFulfilled(this.value);
            // 使用返回值x和新的Promise实例promise2来处理resolve和reject
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            // 如果处理函数抛出异常,则将promise2状态更改为rejected
            reject(error);
          }
        });
      } else if (this.status === 'rejected') {
        // 使用queueMicrotask保证异步调用
        queueMicrotask(() => {
          try {
            // 调用onRejected,并获取返回值
            const x = onRejected(this.reason);
            // 使用返回值x和新的Promise实例promise2来处理resolve和reject
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            // 如果处理函数抛出异常,则将promise2状态更改为rejected
            reject(error);
          }
        });
      } else if (this.status === 'pending') {
        // 如果当前Promise状态仍为pending,将处理函数加入相应的队列中
        this.onFulfilledCallbacks.push(() => {
          // 使用queueMicrotask保证异步调用
          queueMicrotask(() => {
            try {
              // 调用onFulfilled,并获取返回值
              const x = onFulfilled(this.value);
              // 使用返回值x和新的Promise实例promise2来处理resolve和reject
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              // 如果处理函数抛出异常,则将promise2状态更改为rejected
              reject(error);
            }
          });
        });

        this.onRejectedCallbacks.push(() => {
          // 使用queueMicrotask保证异步调用
          queueMicrotask(() => {
            try {
              // 调用onRejected,并获取返回值
              const x = onRejected(this.reason);
              // 使用返回值x和新的Promise实例promise2来处理resolve和reject
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              // 如果处理函数抛出异常,则将promise2状态更改为rejected
              reject(error);
            }
          });
        });
      }
    });

    // 返回新的Promise实例,以便链式调用
    return promise2;
  }

  catch(onRejected) {
    // 调用then方法,仅传入失败处理函数
    return this.then(null, onRejected);
  }

  finally(callback) {
    // 调用then方法,传入两个相同的处理函数
    return this.then(
      (value) => {
        // 创建一个新的Promise实例,确保异步执行callback
        return $Promise$.resolve(callback()).then(() => value);
      },
      (reason) => {
        // 创建一个新的Promise实例,确保异步执行callback
        return $Promise$.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }

  static resolve(value) {
    if (value instanceof $Promise$) {
      return value;
    }
    return new $Promise$((resolve, reject) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new $Promise$((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promises) {
    return new $Promise$((resolve, reject) => {
      const result = [];
      let resolvedCount = 0;

      promises.forEach((promise, index) => {
        $Promise$.resolve(promise).then(
          (value) => {
            result[index] = value;
            resolvedCount++;
            if (resolvedCount === promises.length) {
              resolve(result);
            }
          },
          (reason) => {
            reject(reason);
          }
        );
      });
    });
  }

  static race(promises) {
    return new $Promise$((resolve, reject) => {
      promises.forEach((promise) => {
        $Promise$.resolve(promise).then(
          (value) => {
            resolve(value);
          },
          (reason) => {
            reject(reason);
          }
        );
      });
    });
  }

  static allSettled(promises) {
    return new $Promise$((resolve, reject) => {
      const result = [];
      let settledCount = 0;

      promises.forEach((promise, index) => {
        $Promise$.resolve(promise).then(
          (value) => {
            result[index] = { status: 'fulfilled', value };
            settledCount++;
            if (settledCount === promises.length) {
              resolve(result);
            }
          },
          (reason) => {
            result[index] = { status: 'rejected', reason };
            settledCount++;
            if (settledCount === promises.length) {
              resolve(result);
            }
          }
        );
      });
    });
  }

  static any(promises) {
    return new $Promise$((resolve, reject) => {
      const errors = [];
      let rejectedCount = 0;

      promises.forEach((promise, index) => {
        $Promise$.resolve(promise).then(
          (value) => {
            resolve(value);
          },
          (reason) => {
            errors[index] = reason;
            rejectedCount++;
            if (rejectedCount === promises.length) {
              reject(new AggregateError(errors, 'All promises were rejected'));
            }
          }
        );
      });
    });
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // 1. 如果 promise2 和 x 相同,抛出 TypeError
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 标记是否已调用,防止多次调用
  let called = false;

  // 2. 如果 x 是 $Promise$ 实例
  if (x instanceof $Promise$) {
    // 根据 x 的状态调用 resolve 或 reject
    x.then(
      (y) => {
        resolvePromise(promise2, y, resolve, reject);
      },
      (reason) => {
        reject(reason);
      }
    );
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 3. 如果 x 是对象或函数
    try {
      // 获取 x 的 then 方法
      const then = x.then;
      if (typeof then === 'function') {
        // 如果 then 是函数
        // 使用 x 作为上下文调用 then 方法
        then.call(
          x,
          (y) => {
            // 成功回调
            if (called) return; // 如果已经调用过,直接返回
            called = true;
            // 递归处理 y
            resolvePromise(promise2, y, resolve, reject);
          },
          (reason) => {
            // 失败回调
            if (called) return; // 如果已经调用过,直接返回
            called = true;
            reject(reason);
          }
        );
      } else {
        // 如果 then 不是函数
        // 直接调用 resolve
        resolve(x);
      }
    } catch (error) {
      // 如果获取或调用 then 方法抛出异常
      if (called) return; // 如果已经调用过,直接返回
      called = true;
      reject(error);
    }
  } else {
    // 4. 如果 x 不是对象或函数
    // 直接调用 resolve
    resolve(x);
  }
}

module.exports = $Promise$;

使用promises-aplus-tests套件测试合规性

image.png

image.png

测试代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="./index.js"></script>
    <script>
      //pending promise0
     const promise = new PromisePolyfill((resolve, reject) => {
      //步骤1 执行reject函数 将清空函数队列的任务排入微任务队列,返回了pending状态的promise
        reject('reject');
        resolve('resolve');
      })
      //步骤2 执行同步代码then1 将回调函数排入promise函数数组,返回pending状态的promise1
      //pending promise1
      promise.then(
        (res) => {
          //步骤6 同步代码执行完毕 开始执行promise回调函数,因为此时为reject,所以该函数不会执行,
          //又因为没有传onRejected函数所以Promise内部准备执行onReject函数时,会直接抛出错误,然后被reject掉,理由会传到下一个then的第二个函数的参数
          console.log('onFulfilled被调用',res);
          return 'aaa'
        }
      //步骤3 执行同步代码then2 将回调函数排入promise1函数数组,返回pending状态的promise2
      //pending promise2
      ).then(res => {
          //步骤7,同样的,又因为没有传onRejected函数所以Promise内部准备执行onReject函数时,会直接抛出错误,然后被reject掉,理由会传到下一个then的第二个函数的参数
        console.log('onFulfilled被调用1',res);
        return 'bbb'
      //步骤4 执行同步代码catch2 将回调函数排入promise2函数数组,返回pending状态的promise3
      //pending promise3
      }).catch(err => {
          //步骤8,其实这个catch也是调用then,但是这个then把onRejected函数传入了,所以catch会再次捕获到上面传的错误
        console.log('catch',err);
        return 'bbb'
      //步骤5 执行同步代码then3 将回调函数排入promise3函数数组,返回pending状态的promise4
      //pending promise4
      }).then(res => {
        console.log('catch后面的then,'+res);
      });
      setTimeout(() => {
      console.log(promise);
        promise.then(res => {
          console.log('setTimeout里面的then',res);
        }).catch(err => {
          console.log('setTimeout里面的catch',err);
        })
      },1000)
      PromisePolyfill.resolve('PromisePolyfill.resolve').then(res => {
        console.log(res);
      })
      PromisePolyfill.reject('PromisePolyfill.reject').then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err);
      })
      //当同步任务(宏任务)都执行完 微任务队列将开始执行
    </script>
  </body>
</html>

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...