新闻中心

J*aScript异步编程_全面剖析Promise实现原理

2025-11-18
浏览次数:
返回列表
Promise是异步编程核心,通过状态机和链式调用解决回调地狱;其原理包括状态管理、then方法返回新Promise及resolvePromise处理返回值,实现异步任务的有序执行与错误捕获。

javascript异步编程_全面剖析promise实现原理

J*aScript是单线程语言,为了不阻塞主线程,异步操作成为处理耗时任务(如网络请求、文件读取)的核心机制。早期通过回调函数实现异步,但容易陷入“回调地狱”。ES6引入的Promise极大改善了这一问题。本文将深入剖析Promise的实现原理,帮助你真正理解其工作机制。

Promise 是什么?

Promise 是一个表示异步操作最终完成或失败的对象。它有三种状态:

  • pending(等待中):初始状态,既没有完成也没有拒绝。
  • fulfilled(已成功):操作成功完成。
  • rejected(已失败):操作失败。

一旦状态从 pending 变为 fulfilled 或 rejected,就不会再改变,且这个结果可以被后续通过 then 方法注册的回调函数获取。

手写一个简易 Promise

要理解原理,最好的方式是自己实现一个简化版的 Promise。我们称之为 MyPromise

function MyPromise(executor) {
  let self = this;
  self.status = 'pending';       // 初始状态
  self.value = undefined;        // 成功时的值
  self.reason = undefined;       // 失败时的原因
  self.onResolvedCallbacks = []; // 存储成功的回调
  self.onRejectedCallbacks = []; // 存储失败的回调

  function resolve(value) {
    if (self.status === 'pending') {
      self.status = 'fulfilled';
      self.value = value;
      self.onResolvedCallbacks.forEach(fn => fn());
    }
  }

  function reject(reason) {
    if (self.status === 'pending') {
      self.status = 'rejected';
      self.reason = reason;
      self.onRejectedCallbacks.forEach(fn => fn());
    }
  }

  // 立即执行 executor 函数
  try {
    executor(resolve, reject);
  } catch (err) {
    reject(err); // 如果执行出错,直接进入 reject
  }
}

上面代码定义了 MyPromise 构造函数,接收一个 executor 函数,并立即执行它。resolve 和 reject 用于改变状态并通知所有监听者。

实现 then 方法

then 是 Promise 的核心方法,用于注册成功和失败的回调,并支持链式调用。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let self = this;
  // 解决 onFulfilled、onRejected 不传的情况
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

  // 返回一个新的 Promise,实现链式调用
  let promise2 = new MyPromise((resolve, reject) => {
    if (self.status === 'fulfilled') {
      setTimeout(() => {
        try {
          let x = onFulfilled(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (self.status === 'rejected') {
      setTimeout(() => {
        try {
          let x = onRejected(self.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (self.status === 'pending') {
      // 如果状态还未改变,说明异步操作未完成,先存储回调
      self.onResolvedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(self.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });

      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
  });

  return promise2;
};

关键点:

ChatCut ChatCut

AI视频剪辑工具

ChatCut 1086 查看详情 ChatCut
  • then 必须返回一个新的 Promise,以支持链式调用。
  • 使用 setTimeout 模拟微任务队列,确保回调异步执行。
  • onFulfilled 和 onRejected 可能返回普通值、Promise 或抛出异常,需要统一处理。

resolvePromise 函数:处理返回值

这是 Promise/A+ 规范中最复杂的一部分。它决定如何根据 then 回调的返回值 x 来处理下一个 Promise 的状态。

function resolvePromise(promise2, x, resolve, reject) {
  // 防止循环引用
  if (x === promise2) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  let called = false; // 确保只调用一次 resolve 或 reject

  // 如果 x 是对象或函数,才可能是 Promise
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;

      // 如果 x 是 Promise,则采用它的状态
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject); // 递归解析
        }, r => {
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        // x 是普通对象
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // x 是基本类型值
    resolve(x);
  }
}

该函数处理了各种情况:普通值、Promise、具有 then 方法的“类Promise”对象等,确保符合 Promise/A+ 规范。

静态方法实现

原生 Promise 提供了一些常用静态方法,也可以简单实现:

// MyPromise.resolve
MyPromise.resolve = function(value) {
  return new MyPromise(resolve => resolve(value));
};

// MyPromise.reject
MyPromise.reject = function(reason) {
  return new MyPromise((resolve, reject) => reject(reason));
};

// MyPromise.all
MyPromise.all = function(promises) {
  return new MyPromise((resolve, reject) => {
    let result = [];
    let count = 0;
    if (promises.length === 0) return resolve(result);

    for (let i = 0; i < promises.length; i++) {
      MyPromise.resolve(promises[i]).then(
        val => {
          result[i] = val;
          count++;
          if (count === promises.length) {
            resolve(result);
          }
        },
        err => reject(err)
      );
    }
  });
};

这些方法极大提升了异步编程的表达能力。

基本上就这些。虽然完整实现一个符合规范的 Promise 还涉及更多边界处理,但核心逻辑已经清晰。理解 Promise 的实现原理,不仅能写出更可靠的异步代码,也为学习 async/await 打下坚实基础。

以上就是J*aScript异步编程_全面剖析Promise实现原理的详细内容,更多请关注其它相关文章!


# 如何使用  # 东莞高端网站建设全包  # 温州网站推广哪家有名  # 信阳生产厂家推广营销  # 那个网站做优化比较好  # seo营销推广方案引流  # 相城网站推广哪家强  # 网站怎么样推广  # 婚庆行业网站优化运营  # 合肥网站建设论坛  # 哈尔滨网站建设论文  # 这是  # 是一个  # javascript  # 递归  # 管理器  # 有何  # 返回值  # 有什么  # 链式  # 回调  # 异步任务  # ai  # 回调函数  # java  # es6 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 铁路12306改签能改到更早的车次吗_铁路12306改签提前车次规则  搜狗浏览器如何使用密码生成器创建强密码 搜狗浏览器内置密码安全工具  C++如何解决segmentation fault_C++段错误调试与原因分析  FullCalendar 自定义按钮样式定制指南  126邮箱手机版登录官网2026_126手机邮箱免费入口最新  Centos/Linux 系统下安装 composer 的完整步骤  在J*a中如何使用Exception包装底层异常_异常包装与信息传递方法说明  Windows 11怎么彻底关闭定位_Windows 11服务中禁用Geolocation  PHP高效扁平化嵌套数组:使用array_merge与数组解包操作符  必由学官方登录入口 必由学教师学生账号快速访问  蛙漫2日版入口 WAMAN2(日版)无删减漫画官网链接  深入理解字体排版:Adobe光学字偶距与CSS字偶距的差异与实现  CSS布局:解决全屏元素100%尺寸与外边距导致的页面溢出问题  如何将HTML表格多行数据保存到Google Sheet  纯CSS与HTML网格布局的HTML精简策略:SVG与JS方案解析  铁路12306卧铺选择攻略 铁路12306下铺座位预定技巧  QQ邮箱正确登录入口_QQ邮箱官方网站使用地址  126邮箱账号注册 电脑版登录入口  Django表单验证失败时保留用户输入数据的最佳实践  如何使用Rector自动化升级旧代码_通过Composer安装和配置Rector进行代码重构  批改网学生版PC登录 批改网官网登录系统入口  在J*a中如何在J*a中使用异常机制记录错误日志_异常日志实践经验  Excel中VLOOKUP的第四个参数是干什么用的_Excel VLOOKUP第四参数作用解析  谷歌浏览器无痕模式怎么开 Chrome开启无痕浏览设置方法【教程】  MongoDB Aggregation:在嵌套对象数组中精确匹配ObjectId  京东单号查询入口_京东快递订单追踪入口  如何在 Windows 11 中启动游戏手柄设置  KFC早餐时段怎么领特惠代码_KFC早餐订餐优惠代码获取与使用说明  C++ string find函数返回值npos详解_C++字符串查找失败的判断条件  C++如何实现单例模式_C++设计模式之线程安全的单例写法  Golang如何安装Swagger工具_GoSwagger文档生成环境  红果短剧网页版官网入口 官方最新网址发布  如何使用J*aScript精确选择并批量修改特定父元素下子链接的样式  QQ邮箱网页版登录入口 QQ邮箱官方在线使用平台  动漫岛观看全网网 动漫岛在线正版动漫入口  荣耀Play7T运行卡顿解决_荣耀Play7T性能优化  CSS如何设置hover状态颜色_hover伪类调整背景或文字颜色  CSS Box Model与弹性按钮:维持布局稳定的动画实践  J*aScript中高效管理与清空动态列表:避免循环陷阱  C++如何操作注册表_Windows平台下C++读写注册表的API函数详解  html怎么在cmd下运行php文件_cmd运行html中php文件方法【教程】  优化 Jest 模拟:强制未实现函数抛出错误以提升测试效率  c++如何使用折叠表达式(Fold Expressions)_c++17可变参数模板新技巧  Win11怎么用U盘重装系统 Win11制作启动盘并重装系统完整教程【详解】  Win10如何清理注册表垃圾 Win10手动清理无效注册表【技巧】  在J*a中如何开发在线活动报名与管理系统_活动报名管理项目实战解析  Win11怎么关闭触摸屏_Windows 11禁用HID符合标准触摸屏  PHP表单数据传递:如何通过隐藏输入字段获取动态ID  TikTok搜索不到用户发布内容怎么办 TikTok用户内容搜索优化方法  sublime如何配置Go语言开发环境_sublime搭建Golang编译运行系统 

搜索