Ajax — 异步数据交互

Ajax(Asynchronous JavaScript and XML)是 Web 2.0 时代的核心技术。它让 JS 可以在不刷新页面的情况下,主动向服务器发起 HTTP 请求、获取数据、动态更新页面。

没有 Ajax 之前,任何数据更新都要整页刷新 ------ 体验极差。Ajax 让 "单页应用(SPA)" 成为可能。


1. 数据序列化:JSON.stringify

网络传输的是二进制文本,JS 对象不能直接发送,需要序列化为 JSON 字符串。

js 复制代码
// 服务端:把 JS 对象序列化成 JSON 字符串,发给客户端
const todo = [
  { id: 1, title: '过四六级', completed: false },
  { id: 2, title: '回家过节', completed: false }
];

// JSON.stringify(value, replace?, space?)
// replace: 过滤/替换,传 null 表示原样序列化
// space: 缩进空格数,团队规范,提升可读性
const jsonStr = JSON.stringify(todo, null, 2);
// 结果:格式化的 JSON 字符串,可读性好

三个参数

参数 说明
value 要序列化的 JS 对象
replace? 过滤/替换函数或数组,null 表示原样输出
space? 缩进空格数,推荐 2,团队规范,可读性好

客户端收到后,用 JSON.parse() 还原:

js 复制代码
const todo = JSON.parse(xhr.responseText); // JSON 字符串 → JS 对象

2. JS 异步处理

JS 是单线程 语言 ------ 同一时间只能做一件事。遇到耗时操作(网络请求、定时器、文件读写),不能卡在那里等,必须用异步机制来处理。

2.1 事件循环(Event Loop)

复制代码
同步代码 → 全部执行完
异步任务 → 放入 Event Loop 队列
等同步代码跑完 → 从 Event Loop 里拿出回调函数执行
js 复制代码
console.log('1. 开始');

setTimeout(() => {
  console.log('3. 定时器回调'); // 异步,进入 Event Loop
}, 0);

console.log('2. 结束');

// 输出顺序:1 → 2 → 3
// 即使 setTimeout 设了 0ms,也要等同步代码全部执行完

2.2 三种异步写法

JS 异步处理经历了三代演进:

方式 年代 特点
回调函数(Callback) 古老 嵌套深了变成"回调地狱",难以维护
Promise + then() ES6 链式调用,解决了回调地狱
async / await ES2017 最推荐,写法跟同步代码一样,可读性极佳
js 复制代码
// 方式一:回调函数 --- 最原始
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    // 拿到数据后的处理...
  }
};

// 方式二:Promise + then() --- 好一些
fetch('/todo')
  .then(res => res.json())
  .then(data => {
    // 拿到数据后的处理...
  })
  .catch(err => console.error(err));

// 方式三:async / await --- 最推荐!
const response = await fetch('/todo');
const data = await response.json();
// 跟同步代码看起来一模一样,清晰直观

3. 后端:Node.js HTTP 服务器

用 Node.js 内置的 http 模块创建一个简单的 API 服务器。

js 复制代码
// backend/index.js
// node 内置的 http 模块,用于创建 http 服务器
// require 是 Node.js 早期的模块化系统 CommonJS
// EMS 是升级版:import + export default
const http = require('http');

// 伺服状态 --- http 基于请求-响应模型
http.createServer((req, res) => {
  const todo = [
    { id: 1, title: '过四六级', completed: false },
    { id: 2, title: '回家过节', completed: false }
  ];

  // 设置 CORS 响应头,允许跨域访问
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Content-Type', 'application/json;charset=utf-8');

  // req.url --- 用户请求的路径
  if (req.url === '/') {
    res.end('hello world');
  }

  if (req.url === '/todo') {
    // 序列化为 JSON 字符串后返回
    res.end(JSON.stringify(todo));
  }
}).listen(3000, () => {
  console.log('server is running on 3000 port');
});

启动:

bash 复制代码
node backend/index.js
# 输出: server is running on 3000 port

4. 前端:从 XHR 到 Fetch

4.1 XMLHttpRequest(经典方式)

XMLHttpRequest(XHR)是 Ajax 的底层实现,fetch 的"前辈"。

html 复制代码
<!-- frontend/index.html -->
<body>
  <ul id="todo"></ul>
  <button id="btn">获取todo</button>

  <script>
    document.getElementById('btn')
      .addEventListener('click', () => {
        // 1. 实例化 XHR 对象
        const xhr = new XMLHttpRequest();

        // 2. 打开 HTTP 通道(第三个参数 true = 异步)
        xhr.open('GET', 'http://localhost:3000/todo', true);

        // 3. 监听响应(回调函数 callback)
        xhr.onreadystatechange = function () {
          // readyState: 0→1→2→3→4
          if (xhr.readyState === 4 && xhr.status === 200) {
            const todo = JSON.parse(xhr.responseText);
            document.getElementById('todo')
              .innerHTML = todo
                .map(item => `<li>${item.title}</li>`)
                .join('');
          }
        };

        // 4. 发送请求
        xhr.send();
      });
  </script>
</body>

XHR readyState 状态码

readyState 含义
0 UNSENT --- 还没调用 open
1 OPENED --- 已调用 open
2 HEADERS_RECEIVED --- 已收到响应头
3 LOADING --- 正在接收响应体
4 DONE --- 请求完成

4.2 Fetch + async/await(现代方式)

fetch 是 XHR 的现代替代品,基于 Promise,配合 async/await 使用体验极佳。

html 复制代码
<!-- frontend/fetch.html -->
<body>
  <ul id="todo"></ul>
  <button id="btn">获取todo(Fetch版)</button>

  <script>
    document.getElementById('btn')
      .addEventListener('click', async () => {
        try {
          // fetch 返回 Promise,await 等它完成
          const response = await fetch('http://localhost:3000/todo');
          const todos = await response.json();

          document.getElementById('todo')
            .innerHTML = todos
              .map(todo => `<li>${todo.title}</li>`)
              .join('');
        } catch (error) {
          console.error('请求失败:', error);
        }
      });
  </script>
</body>

对比 XHR:fetch 代码量少了近一半,逻辑更线性、更易读,错误处理也更优雅。


5. 技术演进路线

复制代码
XHR(回调地狱)
  ↓
fetch + Promise.then(链式调用)
  ↓
fetch + async/await(推荐,像同步代码一样写异步逻辑)

核心要点async/await 是目前最优秀的异步处理方式 --- 看起来和同步代码一样直观,但底层依然是异步、非阻塞的。


6. 文件结构

复制代码
ajax/
├── readme.md              ← 本文
├── backend/
│   ├── package.json
│   └── index.js           ← Node.js HTTP 服务器
└── frontend/
    ├── index.html         ← XHR 经典版
    └── fetch.html         ← Fetch + async/await 现代版
相关推荐
Z-D-K2 小时前
考验AI的“自我“-AI对《红楼梦》后40回的改写(32)
人工智能·ai·aigc·交互·agi
工业HMI实战笔记2 小时前
工业HMI界面布局“1核2辅”黄金结构,适配90%场景
前端·ui·性能优化·自动化·交互
想你依然心痛5 小时前
HarmonyOS 6(API 23)实战:基于HMAF的「量子编排」——PC端AI智能体量子计算模拟与量子-经典混合智能编排平台
人工智能·交互·实时音视频·智能体
CYY955 天前
OkHttp 和 Retrofit 封装使用
okhttp·retrofit
CYY956 天前
OkHttp 的使用
okhttp
北极星日淘17 天前
前端 i18n 中日双语交互 + 翻译客服接口联动方案|日系海淘平台中文友好化开发实战
前端·交互
UXbot17 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
蓝速科技17 天前
蓝速科技 AI 数字人部署与交互实战指南
人工智能·科技·交互
朝星17 天前
Android开发[14]:网络优化之OkHttp
android·okhttp·kotlin