🌟 理解现代 JavaScript 异步编程:从 Ajax 到 Promise 与 fetch

49 阅读3分钟

在前端开发的日常中,我们常常需要与服务器通信、处理异步任务。今天的学习内容围绕着 Ajax、Promise、fetch 以及 JavaScript 内存模型 展开,这些都是构建现代 Web 应用不可或缺的核心知识。让我们一起梳理并深入理解这些概念吧!✨


🔄 传统 Ajax 与现代 fetch 的对比

Ajax(Asynchronous JavaScript and XML)

  • 基于 回调函数 实现异步请求。
  • 使用 XMLHttpRequest 对象,代码冗长且嵌套复杂(“回调地狱” 😵)。
  • 需手动处理状态码、响应类型等细节。
// 传统 Ajax 示例
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};
xhr.send();

fetch API

  • 基于 Promise 设计,语法简洁优雅 ✨。
  • 天然支持链式调用(.then().catch()),逻辑清晰。
  • 默认不发送 cookies(需显式配置),更符合现代安全规范。
// fetch 示例
fetch('/api/data')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error('请求失败:', err));

💡 结论fetch 是对 Ajax 的现代化封装,更适合现代开发流程。


🧩 手写一个支持 Promise 的 getJSON 函数

既然 fetch 如此好用,那能否用传统的 XMLHttpRequest 封装出一个返回 Promise 的 getJSON 函数呢?当然可以!

function getJSON(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.onreadystatechange = function () {
      if (xhr.readyState !== 4) return;
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch (e) {
          reject(new Error('JSON 解析失败'));
        }
      } else {
        reject(new Error(`请求失败: ${xhr.status}`));
      }
    };
    xhr.onerror = () => reject(new Error('网络错误'));
    xhr.send();
  });
}

// 使用
getJSON('/api/user')
  .then(user => console.log(user))
  .catch(err => console.error(err));

这个函数将传统的回调式 Ajax 转化为 Promise 风格,实现了“异步变同步”的流程控制 🧠。


🧱 深入理解 Promise

Promise 是 JavaScript 中处理异步操作的事实标准,它有三种状态:

  • pending(初始状态)
  • fulfilled(成功,调用 resolve
  • rejected(失败,调用 reject

通过 .then() 处理成功结果,.catch() 捕获错误,使异步代码更接近同步写法:

new Promise((resolve, reject) => {
  setTimeout(() => resolve('搞定!'), 1000);
})
.then(msg => console.log(msg)) // 输出:搞定!
.catch(err => console.error(err));

关键点:Promise 让异步逻辑线性化,避免了层层嵌套的回调。


💾 JavaScript 内存模型:栈 vs 堆

理解变量存储方式对写出高效代码至关重要:

  • 栈内存(Stack)

    • 存储 基本数据类型(如 number, string, boolean)。
    • 值直接存储,访问速度快 ⚡。
    • 变量提升(hoisting)发生在编译阶段,为变量预留空间。
  • 堆内存(Heap)

    • 存储 引用类型(如 object, array, function)。
    • 栈中保存的是指向堆中对象的 引用地址
    • 对象在堆中分配,内存不连续,访问稍慢。
let a = 10;           // 栈
let obj = { name: 'Alice' }; // obj 在栈,{ name: 'Alice' } 在堆
let b = obj;          // b 和 obj 共享同一堆地址(引用拷贝)

⚠️ 注意:修改 b.name 会影响 obj.name,因为它们指向同一个对象!


🛌 手写 sleep 函数(补充知识点)

虽然你上传的 3.html 只写了“手写sleep函数”,但我们可以实现一个基于 Promise 的 sleep,用于模拟延迟:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
async function demo() {
  console.log('开始');
  await sleep(2000);
  console.log('2秒后执行');
}

这在测试、动画或节流场景中非常实用!🌙


🎯 总结

今天我们从 Ajax 的局限性 出发,拥抱了 Promise 和 fetch 带来的简洁与强大;通过封装 getJSON,掌握了如何将传统异步操作现代化;同时深入理解了 JavaScript 的内存机制,为写出更健壮的代码打下基础。

前端世界日新月异,但底层原理始终如一。掌握这些核心概念,你就能在任何框架(React、Vue、Svelte...)中游刃有余!🚀

继续加油,未来的你一定会感谢现在努力的自己!💪🌈