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 现代版
相关推荐
meilindehuzi_a4 小时前
深入理解 Ajax 异步请求:从 XMLHttpRequest 到 Node.js HTTP 服务实践
http·ajax·node.js
元岳数字人小元16 小时前
AI 数字人开发公司浅谈 虚拟数字人打造景区新服务
人工智能·人机交互·交互
拾年27520 小时前
从零手写 Ajax:用原生 XHR 搭建前后端交互全流程
前端·javascript·ajax
Z-D-K1 天前
考验AI的“自我和意识“-AI对《红楼梦》后40回的改写(21)
人工智能·ai·aigc·交互·agi
互联网散修1 天前
鸿蒙实战:仿小红书“我”的页面——从分层架构到沉浸式交互
交互·harmonyos
2301_764441331 天前
基于AI的本地文件归档智能管理工具梳理
人工智能·python·算法·目标检测·交互
聆思科技AI芯片1 天前
AI语音视觉开发板对接 OpenClaw 龙虾实现多模态交互
人工智能·学习·交互·语音识别·智能硬件
小小龙学IT1 天前
HTMX:让 HTML 重新成为前端核心的超轻量动态交互库
前端·html·交互
2601_954706491 天前
云手机技术原理、API 交互实战与应用场景分析
智能手机·交互