手写promise

promise

javascript 复制代码
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  // 保存初始化状态
  var self = this;

  // 初始化状态
  this.state = PENDING;

  // 用于保存 resolve 或者 rejected 传入的值
  this.value = null;

  // 用于保存 resolve 的回调函数
  this.resolvedCallbacks = [];

  // 用于保存 reject 的回调函数
  this.rejectedCallbacks = [];

  // 状态转变为 resolved 方法
  function resolve(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }

    // 保证代码的执行顺序为本轮事件循环的末尾(可降级用setTimeout)
    queueMicrotask(() => {
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
        // 修改状态
        self.state = RESOLVED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => {
          callback(value);
        });
        self.resolvedCallbacks=[]
      }
    });
  }

  // 状态转变为 rejected 方法
  function reject(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);//手动拆包
    }
    // 保证代码的执行顺序为本轮事件循环的末尾
    queueMicrotask(() => {
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
        // 修改状态
        self.state = REJECTED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => {
          callback(value);
        });
        self.rejectedCallbacks=[]
      }
    });
  }

  // 将两个方法传入函数执行
  try {
    fn(resolve, reject);
  } catch (e) {
    // 遇到错误时,捕获错误,执行 reject 函数
    reject(e);
  }
}

MyPromise.prototype.then = function (onFulfilled, onReject) {
  // 可选参数兜底,不传时提供默认回调
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : val => val;
  onReject = typeof onReject === "function" ? onReject : err => { throw err };

  const self = this;
  // then 必须返回新 Promise 实现链式
  return new MyPromise((resolve, reject) => {
    // 成功封装函数(前一个promise成功时执行的函数)
    let fulfilled = () => {
      try {
        const result = onFulfilled(self.value);
        // 如果回调返回Promise,等待它完成再改变新promise状态
        result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
      } catch (err) {
        reject(err);
      }
    };
    // 失败封装函数(前一个promise失败时执行的函数)
    let rejected = () => {
      try {
        const result = onReject(self.value);
        result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
      } catch (err) {
        reject(err);
      }
    };

    switch (self.state) {
      case PENDING:
        self.resolvedCallbacks.push(fulfilled);
        self.rejectedCallbacks.push(rejected);
        break;
      case RESOLVED:
        fulfilled();
        break;
      case REJECTED:
        rejected();
        break;
    }
  })
};

promise.all

javascript 复制代码
function promiseAll(promises) {
  // 返回一个全新的Promise实例
  return new Promise(function(resolve, reject) {
    // 判断传入参数是否为数组,非数组直接抛出类型错误
    if (!Array.isArray(promises)) {
      throw new TypeError(`argument must be a array`)
    }
    // 记录已成功完成的Promise数量
    let resolvedCounter = 0;
    // 保存传入数组总长度
    let promiseNum = promises.length;
    // 存储所有Promise成功后的结果,下标与入参数组保持一致
    let resolvedResult = [];

     // 处理空数组特殊情况
        if (promiseNum === 0) {
            return resolve([])
        }

    // 循环遍历所有传入的任务
    for (let i = 0; i < promiseNum; i++) {
      // 使用Promise.resolve包装,兼容数组中传入普通值的情况
      Promise.resolve(promises[i])
        .then(value => {
          // 当前任务成功,成功计数+1
          resolvedCounter++;
          // 将结果存入对应下标,保证输出数组顺序和输入一致
          resolvedResult[i] = value;
          // 所有任务全部执行成功
          if (resolvedCounter == promiseNum) {
            // 把完整结果数组resolve出去
            resolve(resolvedResult)
          }
        }, error => {
          // 任意一个任务失败,直接reject失败原因,终止后续处理
          reject(error)
        })
    }
  })
}

promise.race

javascript 复制代码
Promise.race = function (args) {
  // 参数校验
  if (!Array.isArray(args)) {
    throw new TypeError('args must be an array')
  }
  return new Promise((resolve, reject) => {
    for (let i = 0, len = args.length; i < len; i++) {
      // 使用Promise.resolve包装,兼容普通值
      Promise.resolve(args[i]).then(resolve, reject)
    }
  })
}

promise.allSettled

javascript 复制代码
Promise.allSettled = function (promises) {
    // 参数校验
    if (!Array.isArray(promises)) {
        throw new TypeError('promises must be an array')
    }
    return new Promise((resolve) => {
        let count = 0
        const result = []
        const len = promises.length

        // 边界:空数组直接返回
        if (len === 0) {
            return resolve([])
        }

        for (let i = 0; i < len; i++) {
            // Promise.resolve 兼容普通非Promise值
            Promise.resolve(promises[i])
                .then((value) => {
                    result[i] = {
                        status: 'fulfilled',
                        value
                    }
                })
                .catch((reason) => {
                    result[i] = {
                        status: 'rejected',
                        reason
                    }
                })
                .finally(() => {
                    count++
                    // 全部处理完成
                    if (count === len) {
                        resolve(result)
                    }
                })
        }
    })
}
相关推荐
wangxiaoxuan21730 分钟前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界
文丑颜不良啊1 小时前
LeetCode2821~2840题目解析
java·leetcode
apcipot_rain1 小时前
C++转Java日记(10)
java·开发语言
总要学点什么2 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
简 洁 冬冬2 小时前
泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」
java·泛型
LGL6030A2 小时前
Java学习历程31——初识Spring Boot
java·spring boot·学习
Agent世界3 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo3 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
总要学点什么4 小时前
第104篇:三维特效-流动墙体效果——让贴图沿墙流动,它却只肯走一条对角线
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
某哈压力大4 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行