LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

手写 Ajax 与 Promise:从底层原理到实际应用

liguoquan
2025年7月9日 15:33 本文热度 53
:手写 Ajax 与 Promise:从底层原理到实际应用


手写 Ajax 与 Promise:从底层原理到实际应用

​在前端开发中,异步请求Promise是绕不开的核心知识点。无论是获取数据、提交表单,还是处理复杂的业务逻辑,我们都需要与异步操作打交道。本文将通过手写 Ajax 请求和解析 Promise 的底层原理,结合生活中的实际案例,带你深入理解这些技术的本质。


一、Ajax 的本质:异步通信的基石

1.1 什么是 Ajax?

Ajax(Asynchronous JavaScript and XML)是一种通过 JavaScript 与服务器进行异步通信的技术。它允许页面在不刷新的情况下动态更新数据,极大提升了用户体验。

生活类比
想象你在点外卖。你下单后,不需要一直盯着手机等外卖送到,而是继续做其他事情。外卖送达时,系统会通知你。这个“异步等待”的过程,就是 Ajax 的核心思想。


1.2 手写 Ajax 请求

步骤一:创建 XMLHttpRequest 对象

function createAjaxRequest() {  if (window.XMLHttpRequest) {    return new XMLHttpRequest(); // 现代浏览器  } else {    return new ActiveXObject("Microsoft.XMLHTTP"); // 兼容 IE6-IE11  } }

步骤二:发送 GET 请求

function getWeather(city) {  const xhr = createAjaxRequest();  xhr.open("GET", `https://api.example.com/weather?city=${city}`, true);  xhr.onreadystatechange = function () {    if (xhr.readyState === 4 && xhr.status === 200) {      console.log("成功获取天气数据:", JSON.parse(xhr.responseText));    } else if (xhr.readyState === 4) {      console.error("请求失败,状态码:", xhr.status);    }  };  xhr.send(); } // 调用示例 getWeather("北京");

代码解析:

  • open():初始化请求,指定方法(GET/POST)、URL 和是否异步。
  • onreadystatechange:监听请求状态变化。
  • send():发送请求。

1.3 手写 Ajax 的痛点

上述代码虽然能工作,但存在以下问题:

  1. 重复代码:每次请求都要手动处理状态判断和错误处理。
  2. 回调地狱:多个异步操作嵌套会导致代码难以维护。
  3. 缺乏统一接口:不同浏览器的兼容性处理复杂。

生活类比
这就像每次点外卖都要自己跑厨房、打包、配送。效率低且容易出错。


二、Promise 的底层原理:优雅处理异步的“乐高积木”

2.1 Promise 的核心思想

Promise 是一种异步编程的容器,它将异步操作的结果(成功或失败)封装成一个对象,通过链式调用和统一的接口管理异步流程。

生活类比
Promise 像是一张“承诺书”。你告诉服务器:“我需要数据”,服务器承诺在某个时间点给你结果。无论成功还是失败,你都可以通过 .then() 或 .catch() 处理。


2.2 手写 Promise 的核心逻辑

步骤一:定义 Promise 的状态

class MyPromise {  constructor(executor) {    this.status = "pending"; // 初始状态    this.value = undefined; // 成功值    this.reason = undefined; // 失败原因    this.onFulfilledCallbacks = []; // 成功回调队列    this.onRejectedCallbacks = []; // 失败回调队列    const resolve = (value) => {      if (this.status === "pending") {        this.status = "fulfilled";        this.value = value;        // 触发所有成功回调        this.onFulfilledCallbacks.forEach((fn) => fn());      }    };    const reject = (reason) => {      if (this.status === "pending") {        this.status = "rejected";        this.reason = reason;        // 触发所有失败回调        this.onRejectedCallbacks.forEach((fn) => fn());      }    };    try {      executor(resolve, reject); // 执行用户传入的函数    } catch (error) {      reject(error); // 捕获同步错误    }  }  then(onFulfilled, onRejected) {    if (this.status === "fulfilled") {      onFulfilled(this.value);    } else if (this.status === "rejected") {      onRejected(this.reason);    } else {      // 异步情况下,先将回调存入队列      this.onFulfilledCallbacks.push(() => onFulfilled(this.value));      this.onRejectedCallbacks.push(() => onRejected(this.reason));    }  } }

代码解析:

  • 状态管理:Promise 有三种状态(pendingfulfilledrejected),状态一旦改变不可逆。
  • 回调队列:当 Promise 处于 pending 状态时,先将回调函数暂存,等到状态改变后再执行。
  • 错误处理:通过 try-catch 捕获同步错误,避免程序崩溃。

2.3 手写 Promise 的优化:链式调用

class MyPromise {  // ... 上述代码省略 ...  then(onFulfilled, onRejected) {    const promise2 = new MyPromise((resolve, reject) => {      if (this.status === "fulfilled") {        setTimeout(() => {          try {            const x = onFulfilled(this.value);            resolve(x); // 返回新 Promise          } catch (e) {            reject(e);          }        }, 0);      } else if (this.status === "rejected") {        setTimeout(() => {          try {            const x = onRejected(this.reason);            resolve(x);          } catch (e) {            reject(e);          }        }, 0);      } else {        // 异步情况下,先将回调存入队列        this.onFulfilledCallbacks.push(() => {          setTimeout(() => {            try {              const x = onFulfilled(this.value);              resolve(x);            } catch (e) {              reject(e);            }          }, 0);        });        this.onRejectedCallbacks.push(() => {          setTimeout(() => {            try {              const x = onRejected(this.reason);              resolve(x);            } catch (e) {              reject(e);            }          }, 0);        });      }    });    return promise2;  } }

代码解析:

  • 链式调用:每个 .then() 返回一个新的 Promise 实例,实现链式调用。
  • 微任务队列:使用 setTimeout(fn, 0) 将回调放入微任务队列,确保异步执行顺序正确。

三、结合 Ajax 的 Promise 封装

3.1 用 Promise 封装 Ajax 请求

function getWeather(city) {  return new MyPromise((resolve, reject) => {    const xhr = createAjaxRequest();    xhr.open("GET", `https://api.example.com/weather?city=${city}`, true);    xhr.onreadystatechange = function () {      if (xhr.readyState === 4) {        if (xhr.status === 200) {          resolve(JSON.parse(xhr.responseText));        } else {          reject(`请求失败,状态码: ${xhr.status}`);        }      }    };    xhr.send();  }); } // 使用示例 getWeather("北京")  .then((data) => {    console.log("成功获取天气数据:", data);    return getWeather("上海"); // 链式调用  })  .then((data) => {    console.log("成功获取上海天气数据:", data);  })  .catch((error) => {    console.error("请求失败:", error);  });

代码解析:

  • 封装逻辑:将 Ajax 请求封装为 Promise,统一处理成功和失败。
  • 链式调用:通过 .then() 实现多个异步操作的串联。
  • 错误捕获:通过 .catch() 统一处理所有错误,避免嵌套回调。

3.2 生活类比:从“点外卖”到“智能配送”

  • 未使用 Promise:每次点外卖都要手动查看配送状态,代码嵌套复杂。
  • 使用 Promise:系统自动通知你配送结果,你可以专注于其他事情,无需频繁检查。

四、Promise 的底层原理详解

4.1 Promise 的状态管理

状态描述
pending初始状态,既不是成功也不是失败
fulfilled操作成功完成
rejected操作失败

关键点:状态一旦改变,不可逆;Promise 只能有一个最终结果。


4.2 微任务队列与事件循环

Promise 的 .then() 和 .catch() 方法会在微任务队列中执行,优先级高于宏任务(如 setTimeout)。这是 Promise 实现异步流程控制的关键。

console.log("Start"); // 同步代码 new MyPromise((resolve) => {  console.log("Promise executor"); // 同步代码  resolve(); }).then(() => {  console.log("Promise then"); // 微任务 }); setTimeout(() => {  console.log("Timeout"); // 宏任务 }, 0); console.log("End"); // 同步代码 // 输出顺序: // Start // Promise executor // End // Promise then // Timeout

生活类比:

  • 同步代码:像排队点餐,必须等前面的人处理完才能轮到你。
  • 微任务:像餐厅的快速通道,优先处理。
  • 宏任务:像普通排队,需等待其他任务完成。

4.3 链式调用的实现原理

每个 .then() 返回一个新的 Promise 实例,形成链式结构。通过递归调用 then,可以实现异步操作的串联。

new MyPromise((resolve) => {  resolve(1); })  .then((value) => {    console.log(value); // 1    return value + 1;  })  .then((value) => {    console.log(value); // 2    return value + 1;  })  .then((value) => {    console.log(value); // 3  });

五、总结:从“手写”到“理解”

5.1 核心收获

  1. Ajax 的本质:通过 XMLHttpRequest 实现异步通信,但存在代码冗余和回调地狱的问题。
  2. Promise 的优势:通过状态管理和链式调用,简化异步代码,提升可维护性。
  3. 底层原理:Promise 通过微任务队列和回调队列管理异步流程,状态不可逆且只能有一个结果。

5.2 实际应用建议

  • 封装 Ajax:将重复逻辑抽离为 Promise,提高代码复用性。
  • 避免回调地狱:使用 .then() 和 .catch() 替代嵌套回调。
  • 理解事件循环:掌握微任务和宏任务的执行顺序,避免异步逻辑错误。

六、结语

手写 Ajax 和 Promise 不仅是面试高频考点,更是深入理解前端异步编程的关键。通过本文的实践和解析,希望你能真正掌握这些技术的核心思想,并在实际项目中灵活运用。如果觉得内容对你有帮助,欢迎点赞、收藏,或分享给更多开发者!


该文章在 2025/7/9 15:33:45 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved