手写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)
                    }
                })
        }
    })
}
相关推荐
CallFay云起未来5 小时前
AI客服上线后多久才能回本?从TCO到ROI的完整测算方法
java·大数据·人工智能·架构·文心一言
Sam_Deep_Thinking5 小时前
什么是CountDownLatch?
java·后端·面试·程序员
liangshanbo12155 小时前
面试题:常见的前端构建工具有哪些?分别适用于什么场景?
前端
小蒜学长5 小时前
基于SpringBoot和Vue的低卡食品销售系统的设计与实现(代码+数据库+LW)
java·后端·springboot·健康管理·低卡食品销售系统
CRMEB系统商城5 小时前
前后端技术栈全面换代!CRMEB 多商户(Java)v3.0更新预告
java·开发语言·小程序·php
事圆则缓6 小时前
Flutter 状态管理框架对比(三):Riverpod 如何连起购物车与异步价格
开发语言·javascript·flutter
linux_cfan6 小时前
videojs v10 源代码系列解读:30 · `SerialRunner` 与 `ConcurrentRunner`
前端
liangshanbo12156 小时前
面试题:页面需要发起大量 Ajax / HTTP 请求时,如何优化请求性能?
前端·http·ajax
Sayai6 小时前
Neo4j 内嵌模式(Embedded)实战:Java 嵌入式 vs 服务端部署的写入性能对比与 GC 调优
java·开发语言·性能优化·neo4j·图数据库
lyz2468596 小时前
vue3页面因参数未声明而报错的常犯错误
前端·javascript·vue.js