深入理解 Ajax 异步请求:从 XMLHttpRequest 到 Node.js HTTP 服务实践
深入理解 Ajax 异步请求:从 XMLHttpRequest 到 Node.js HTTP 服务实践
- 前言
- 一、什么是 Ajax
- 二、JavaScript 为什么需要异步
- 三、Event Loop 事件循环机制
- 四、JSON 数据格式
- 五、使用 Node.js 搭建 HTTP 服务
- 六、模拟 Todo 数据
- 七、实现路由功能
- 八、实现 Todo 接口
- 九、解决跨域问题(CORS)
- 十、设置响应数据类型
- 十一、返回 Todo 数据
- 十二、监听端口
- 十三、完整服务端代码
- 十四、使用 XMLHttpRequest 发起 Ajax 请求
- 十五、页面结构分析
- 十六、事件注册机制
- 十七、Ajax 的发展历史
- 十八、创建 XMLHttpRequest 对象
- 十九、配置请求
- 二十、send() 发送请求
- 二十一、请求状态监听
- 二十二、readyState 状态详解
- 二十三、HTTP 状态码
- 二十四、解析服务器返回的数据
- 二十五、动态渲染页面
- 二十六、完整请求流程分析
- 二十七、为什么 Ajax 能实现局部刷新
- 二十八、Fetch:现代 Ajax 方案
- 二十九、Async 与 Await
- 总结
前言
在现代 Web 开发中,前后端数据交互是最基础也是最重要的能力之一。当我们打开一个网页时,页面中的数据通常并不是直接写死在 HTML 中,而是通过网络请求从服务器动态获取。
例如:
- 待办事项列表
- 用户信息
- 商品数据
- 新闻内容
这些数据往往存储在服务器或数据库中,需要浏览器主动发起请求,再由服务器返回对应的数据。
如果每获取一次数据都重新加载整个页面,那么用户体验将会非常差。因此,Ajax 技术应运而生。
本文将通过一个完整的 TodoList 案例,从 HTTP 服务搭建开始,逐步讲解:
- Ajax 的核心思想
- JavaScript 异步机制
- Event Loop 事件循环
- JSON 数据格式
- Node.js HTTP 服务
- XMLHttpRequest 工作原理
- DOM 动态渲染
- Fetch 与 Async/Await
帮助大家建立完整的前后端通信知识体系。
一、什么是 Ajax
Ajax 全称:
Asynchronous JavaScript And XML
中文通常翻译为:
异步 JavaScript 与 XML
虽然名字中带有 XML,但现代开发中几乎已经全面使用 JSON 作为数据交换格式。
Ajax 的核心思想非常简单:
页面不刷新
↓
发送网络请求
↓
获取服务器数据
↓
局部更新页面
1.1 传统网页的工作方式
早期 Web 页面采用的是同步刷新模式。
例如点击一个超链接:
<a href="/news">新闻列表</a>
执行流程如下:
用户点击链接
↓
浏览器发送请求
↓
服务器返回新页面
↓
浏览器重新加载整个页面
即使只是更新一小部分内容,也必须重新加载整个页面。
这种方式存在两个明显问题:
- 页面闪烁
- 用户体验较差
1.2 Ajax 的出现
Ajax 技术出现后,浏览器获得了主动向服务器发送请求的能力。
执行流程变成:
用户操作页面
↓
发送异步请求
↓
服务器返回数据
↓
JavaScript更新DOM
↓
页面局部刷新
例如:
用户点击查看待办事项:
点击按钮
↓
请求 /todos
↓
获取JSON数据
↓
更新列表区域
↓
页面其余部分保持不变
这也是现代 Web 应用能够实现流畅交互的重要原因。
二、JavaScript 为什么需要异步
理解 Ajax 之前,必须先理解 JavaScript 的执行机制。
2.1 JavaScript 是单线程语言
JavaScript 只有一个主线程。
所谓单线程,就是:
同一时刻只能执行一个任务
例如:
console.log("A");
console.log("B");
console.log("C");
输出结果:
A
B
C
所有代码严格按照顺序执行。
2.2 如果没有异步会发生什么
假设浏览器发送一个网络请求:
const data = request();
服务器需要 5 秒钟才能返回结果。
如果 JavaScript 采用同步执行模式,那么:
请求发送
↓
等待5秒
↓
收到数据
↓
继续执行
在等待期间:
- 页面无法响应
- 按钮无法点击
- 用户无法操作
整个浏览器都会被阻塞。
显然这是无法接受的。
因此 JavaScript 引入了异步机制。
2.3 同步与异步
同步执行:
console.log(1);
console.log(2);
console.log(3);
输出:
1
2
3
异步执行:
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
输出:
1
3
2
这里的关键问题是:
为什么明明先写了 setTimeout,却最后执行?
答案就在 Event Loop 中。
三、Event Loop 事件循环机制
JavaScript 并不会等待异步任务完成。
例如:
console.log("start");
setTimeout(() => {
console.log("timeout");
}, 1000);
console.log("end");
输出:
start
end
timeout
3.1 执行过程分析
第一步:
console.log("start");
输出:
start
第二步:
setTimeout(...)
发现是异步任务。
JavaScript 不会等待。
而是交给浏览器处理。
第三步:
继续执行后续代码:
console.log("end");
输出:
end
第四步:
1 秒后计时结束。
回调函数进入任务队列。
第五步:
Event Loop 检测调用栈为空。
将回调函数取出执行。
输出:
timeout
3.2 Event Loop 工作流程
整个过程如下:
主线程(Call Stack)
↓
遇到异步任务
↓
交给浏览器(Web APIs)
↓
任务完成
↓
进入任务队列(Task Queue)
↓
Event Loop检测
↓
重新进入主线程执行
可以简化表示为:
Call Stack
↓
Web APIs
↓
Task Queue
↓
Event Loop
↓
Call Stack
3.3 Ajax 与 Event Loop 的关系
Ajax 请求本质也是异步任务。
例如:
xhr.send();
发送请求后:
浏览器开始请求服务器
↓
JavaScript继续执行
↓
服务器返回数据
↓
触发回调函数
↓
处理响应结果
因此:
发送请求
≠
等待响应
这一点非常重要。
很多初学者误以为:
xhr.send();
执行后程序会停下来等待服务器返回。
实际上并不会。
JavaScript 会继续执行后面的代码。
四、JSON 数据格式
浏览器和服务器之间交换数据时,最常使用的数据格式就是 JSON。
JSON 全称:
JavaScript Object Notation
即:
JavaScript对象表示法
4.1 JSON 为什么存在
例如:
const todo = {
id:1,
title:"过四六级",
completed:false
};
这是一个 JavaScript 对象。
但是网络无法直接传输对象。
HTTP 能传输的本质上是:
字符串
或
二进制数据
因此需要将对象转换为字符串。
这个过程称为:
序列化(Serialization)
4.2 JSON.stringify()
作用:
JavaScript对象
↓
JSON字符串
例如:
const todo = {
id:1,
title:"过四六级"
};
console.log(
JSON.stringify(todo)
);
输出:
{"id":1,"title":"过四六级"}
此时对象已经变成字符串,可以通过网络发送。
4.3 replace 参数
语法:
JSON.stringify(value, replace)
用于控制哪些属性参与序列化。
例如:
const todo = {
id:1,
title:"过四六级",
completed:false
};
console.log(
JSON.stringify(
todo,
["title"]
)
);
输出:
{"title":"过四六级"}
只有 title 被保留。
replace 还可以接收函数:
JSON.stringify(
todo,
function(key,value){
return value;
}
);
这种写法可以对序列化过程进行更加灵活的控制。
4.4 space 参数
语法:
JSON.stringify(
value,
null,
space
);
用于控制缩进。
例如:
JSON.stringify(todo,null,2);
输出:
{
"id": 1,
"title": "过四六级"
}
开发阶段经常使用:
JSON.stringify(data,null,2);
因为格式更加清晰。
但在实际网络传输时通常不会添加缩进。
原因是:
缩进越多
↓
字符串越长
↓
传输体积越大
4.5 JSON.parse()
作用:
JSON字符串
↓
JavaScript对象
例如:
const str =
'{"id":1,"title":"过四六级"}';
const obj =
JSON.parse(str);
console.log(obj.title);
输出:
过四六级
4.6 stringify 与 parse 的对应关系
服务器发送数据:
对象
↓
JSON.stringify()
↓
JSON字符串
↓
网络传输
浏览器接收数据:
JSON字符串
↓
JSON.parse()
↓
对象
这是前后端通信过程中最基础也是最重要的一组 API。
五、使用 Node.js 搭建 HTTP 服务
在了解 Ajax 请求原理之前,我们首先需要拥有一个能够提供数据的服务器。
在实际开发中,前端页面的数据通常来自:
- MySQL
- PostgreSQL
- MongoDB
- Redis
等数据库。
但为了更专注于理解 HTTP 通信过程,本文使用 Node.js 原生 HTTP 模块模拟后端接口。
5.1 Node.js 内置 HTTP 模块
Node.js 提供了内置的 http 模块。
无需安装即可直接使用。
const http = require("http");
什么是模块(Module)
随着项目规模增大,不可能将所有代码都写在一个文件中。
例如:
project
│
├── app.js
├── user.js
├── todo.js
└── utils.js
每个文件负责不同功能。
这种开发方式称为:
模块化开发
CommonJS 模块规范
Node.js 早期采用 CommonJS 规范。
导入模块:
const http = require("http");
导出模块:
module.exports = data;
ES Module 模块规范
现代 JavaScript 推荐使用:
import http from "http";
导出:
export default data;
对应关系如下:
| CommonJS | ES Module |
|---|---|
| require | import |
| module.exports | export |
| Node早期方案 | ECMAScript官方方案 |
5.2 创建 HTTP 服务器
代码如下:
const http = require("http");
http.createServer((req,res)=>{
});
这里调用了:
http.createServer()
用于创建一个 HTTP 服务。
createServer 的工作原理
服务器启动后:
等待请求
当浏览器访问:
http://localhost:3000
时:
浏览器发送请求
↓
Node接收请求
↓
执行回调函数
因此:
(req,res)=>{
}
并不是服务器启动时执行。
而是在收到请求时执行。
这一点需要特别注意。
5.3 req 与 res
createServer 回调函数有两个参数:
(req,res)
req(Request)
表示请求对象。
保存客户端发送过来的信息。
例如:
req.url
获取访问路径。
访问:
http://localhost:3000/
得到:
req.url
// "/"
访问:
http://localhost:3000/todos
得到:
req.url
// "/todos"
除了 url 之外,后续开发中还会经常使用:
req.method
请求方式。
例如:
GET
POST
PUT
DELETE
res(Response)
表示响应对象。
负责向浏览器返回数据。
例如:
res.end()
表示:
结束响应并返回数据
六、模拟 Todo 数据
为了模拟数据库查询结果。
我们定义一个数组:
const todos = [
{
id:1,
title:"过四六级",
completed:false
},
{
id:2,
title:"回家过节",
completed:false
}
];
这里很多初学者容易误解。
实际上:
这不是数据库
而只是:
内存中的普通数组
用于模拟数据库返回的数据。
真实项目中通常是:
SELECT * FROM todos;
查询数据库后再返回结果。
七、实现路由功能
完整代码:
if(req.url === "/"){
res.end("hello world");
}
7.1 什么是路由
路由(Route)本质上就是:
不同URL
↓
对应不同处理逻辑
例如:
/
首页
/todos
任务列表
/users
用户列表
因此:
if(req.url === "/"){
}
实际上就是最简单的路由实现。
7.2 首页路由
if(req.url === "/"){
res.end("hello world");
}
访问:
http://localhost:3000/
浏览器显示:
hello world
八、实现 Todo 接口
继续添加:
if(req.url === "/todos"){
}
当用户访问:
http://localhost:3000/todos
时进入对应逻辑。
8.1 为什么需要响应头
服务器返回数据时。
除了返回内容本身。
还会返回大量描述信息。
例如:
内容类型
编码方式
缓存策略
跨域规则
这些信息称为:
HTTP响应头
九、解决跨域问题(CORS)
代码:
res.setHeader(
"Access-Control-Allow-Origin",
"*"
);
9.1 什么是同源策略
假设:
前端运行在:
http://localhost:5500
后端运行在:
http://localhost:3000
虽然都在本机。
但是:
端口不同
浏览器认为:
不是同一个源
因此会触发:
跨域限制
浏览器默认禁止:
fetch("http://localhost:3000/todos");
直接访问。
控制台会报错:
CORS Error
9.2 Access-Control-Allow-Origin
为了解决跨域问题。
服务器需要主动告诉浏览器:
允许访问
代码:
res.setHeader(
"Access-Control-Allow-Origin",
"*"
);
表示:
允许所有来源访问
开发阶段经常使用:
"*"
生产环境通常会指定具体域名。
例如:
res.setHeader(
"Access-Control-Allow-Origin",
"https://example.com"
);
安全性更高。
十、设置响应数据类型
代码:
res.setHeader(
"Content-Type",
"application/json"
);
10.1 为什么需要 Content-Type
浏览器收到数据后。
必须知道:
返回内容是什么类型
否则无法正确解析。
常见类型:
HTML
text/html
普通文本
text/plain
JSON
application/json
图片
image/png
视频
video/mp4
因此:
application/json
告诉浏览器:
我返回的是JSON数据
十一、返回 Todo 数据
代码:
res.end(
JSON.stringify(todos)
);
11.1 为什么不能直接返回对象
很多初学者会尝试:
res.end(todos);
实际上这是错误的。
原因在于:
HTTP 协议只能传输:
字符串
或
二进制数据
而:
[]
{}
属于 JavaScript 对象。
无法直接传输。
11.2 数据序列化过程
因此需要:
JSON.stringify()
进行序列化。
转换过程:
[
{
id:1,
title:"过四六级"
}
]
↓
[
{
"id":1,
"title":"过四六级"
}
]
↓
JSON字符串
↓
通过HTTP发送
十二、监听端口
代码:
.listen(3000,()=>{
console.log(
"server is running on 3000 port"
);
});
12.1 什么是端口
可以把:
IP地址
理解为:
一栋大楼
把:
Port
理解为:
房间号
例如:
127.0.0.1:3000
表示:
本机
↓
3000号端口
访问:
http://localhost:3000
实际上就是访问:
本机3000端口上的HTTP服务
十三、完整服务端代码
const http = require("http");
http.createServer((req,res)=>{
const todos = [
{
id:1,
title:"过四六级",
completed:false
},
{
id:2,
title:"回家过节",
completed:false
}
];
if(req.url === "/"){
res.end("hello world");
}
if(req.url === "/todos"){
res.setHeader(
"Access-Control-Allow-Origin",
"*"
);
res.setHeader(
"Content-Type",
"application/json"
);
res.end(
JSON.stringify(todos)
);
}
}).listen(3000,()=>{
console.log(
"server is running on 3000 port"
);
});
十四、使用 XMLHttpRequest 发起 Ajax 请求
服务端接口已经搭建完成。
接下来,我们开始编写前端代码,通过 Ajax 获取服务器中的 Todo 数据。
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="todos"></ul>
<button id="btn">按钮</button>
<script>
console.log("start");
document
.getElementById("btn")
.addEventListener("click",function(){
console.log("点击按钮");
});
const xhr = new XMLHttpRequest();
xhr.open(
"GET",
"http://localhost:3000/todos",
true
);
console.log("start");
xhr.onreadystatechange = function(){
console.log(xhr.readyState);
if(
xhr.status === 200 &&
xhr.readyState === 4
){
const todos =
JSON.parse(
xhr.responseText
);
document
.getElementById("todos")
.innerHTML =
todos
.map(todo =>
`<li>${todo.title}</li>`
)
.join("");
}
}
console.log("end");
xhr.send();
</script>
</body>
</html>
十五、页面结构分析
首先观察页面结构:
<ul id="todos"></ul>
<button id="btn">按钮</button>
页面中只有两个元素:
一个用于显示任务列表:
<ul id="todos"></ul>
一个用于演示事件机制:
<button id="btn">按钮</button>
15.1 Todos 容器
初始状态:
<ul id="todos"></ul>
页面中没有任何数据。
当服务器返回数据后:
<ul id="todos">
<li>过四六级</li>
<li>回家过节</li>
</ul>
内容会动态生成。
这正是 Ajax 的核心思想:
数据驱动页面
而不是:
把数据写死在HTML中
十六、事件注册机制
代码:
document
.getElementById("btn")
.addEventListener("click",function(){
console.log("点击按钮");
});
16.1 为什么需要事件监听
浏览器无法提前知道:
用户什么时候点击按钮
因此需要提前注册事件。
执行流程:
注册事件
↓
等待用户操作
↓
事件触发
↓
执行回调函数
16.2 addEventListener 参数分析
第一个参数:
"click"
表示事件类型。
常见事件:
"click"
点击事件
"input"
输入事件
"change"
内容改变事件
"submit"
表单提交事件
第二个参数:
function(){
}
回调函数。
只有事件发生时才会执行。
十七、Ajax 的发展历史
在学习 XMLHttpRequest 之前。
有必要了解 Ajax 技术的发展过程。
17.1 Web 1.0 时代
早期网页主要依赖:
<a href="">
和:
<form>
进行页面跳转。
执行流程:
点击链接
↓
浏览器发送请求
↓
服务器返回新页面
↓
浏览器重新刷新
整个页面都会被重新加载。
17.2 Ajax 时代
XMLHttpRequest 出现后。
浏览器获得了主动请求服务器的能力。
执行流程变成:
发送请求
↓
获取数据
↓
更新页面局部区域
↓
无需刷新页面
这就是 Ajax 的本质。
17.3 Fetch 时代
现代浏览器逐渐采用:
fetch()
代替:
XMLHttpRequest
虽然 API 不同。
但核心思想完全一致:
发送HTTP请求
↓
获取服务器数据
↓
更新页面
十八、创建 XMLHttpRequest 对象
代码:
const xhr = new XMLHttpRequest();
18.1 XMLHttpRequest 是什么
可以理解为:
浏览器中的HTTP客户端
专门负责:
- 建立连接
- 发送请求
- 接收响应
创建对象后:
xhr
就拥有了完整的 HTTP 通信能力。
十九、配置请求
代码:
xhr.open(
"GET",
"http://localhost:3000/todos",
true
);
19.1 第一个参数
"GET"
请求方式。
常见请求方式:
GET
获取数据
GET /todos
POST
创建数据
POST /todos
PUT
修改数据
PUT /todos/1
DELETE
删除数据
DELETE /todos/1
19.2 第二个参数
"http://localhost:3000/todos"
请求地址。
对应服务端:
if(req.url === "/todos")
19.3 第三个参数
true
表示异步请求。
如果写成:
false
则表示同步请求。
浏览器会被阻塞。
现代开发已经基本废弃同步请求。
二十、send() 发送请求
代码:
xhr.send();
作用:
正式发送HTTP请求
很多初学者认为:
xhr.send();
执行后程序会停下来等待。
实际上不会。
例如:
console.log("A");
xhr.send();
console.log("B");
输出:
A
B
说明:
请求发送
≠
等待响应
这正是 Ajax 的异步特性。
二十一、请求状态监听
代码:
xhr.onreadystatechange = function(){
}
21.1 什么是 onreadystatechange
每当请求状态发生变化。
浏览器都会触发:
onreadystatechange
回调函数。
因此:
console.log(xhr.readyState);
会输出多个数字。
因为请求过程经历了多个阶段。
二十二、readyState 状态详解
XMLHttpRequest 一共有五种状态。
0:UNSENT
对象创建完成
例如:
const xhr =
new XMLHttpRequest();
1:OPENED
已经调用open()
例如:
xhr.open(...)
2:HEADERS_RECEIVED
已经收到响应头
服务器已经响应。
但数据还未接收完成。
3:LOADING
正在接收数据
数据不断从服务器传输过来。
4:DONE
请求完成
数据接收结束。
可以开始处理结果。
因此:
console.log(xhr.readyState);
通常会看到:
1
2
3
4
或者:
2
3
3
4
具体取决于网络环境。
二十三、HTTP 状态码
代码:
xhr.status === 200
表示:
HTTP响应状态码
常见状态码:
200
OK
请求成功
404
Not Found
资源不存在
500
Internal Server Error
服务器内部错误
因此:
xhr.status === 200 &&
xhr.readyState === 4
表示:
请求成功完成
此时可以安全处理数据。
二十四、解析服务器返回的数据
代码:
const todos =
JSON.parse(
xhr.responseText
);
24.1 responseText 是什么
服务器返回:
[
{
"id":1,
"title":"过四六级"
}
]
很多人误以为:
responseText
得到的是数组。
实际上不是。
得到的是:
JSON字符串
例如:
'[{"id":1,"title":"过四六级"}]'
本质仍然是字符串。
24.2 JSON.parse
因此需要:
JSON.parse()
进行反序列化。
转换过程:
JSON字符串
↓
JSON.parse()
↓
JavaScript对象
这与服务端的:
JSON.stringify()
形成对应关系。
服务端:
对象
↓
JSON.stringify()
↓
字符串
浏览器:
字符串
↓
JSON.parse()
↓
对象
二十五、动态渲染页面
获取数据后:
todos.map(
todo =>
`<li>${todo.title}</li>`
)
假设数据:
[
{
title:"过四六级"
},
{
title:"回家过节"
}
]
生成:
[
"<li>过四六级</li>",
"<li>回家过节</li>"
]
随后:
.join("")
转换为:
<li>过四六级</li><li>回家过节</li>
最终:
document
.getElementById("todos")
.innerHTML = ...
更新页面。
浏览器渲染结果:
• 过四六级
• 回家过节
二十六、完整请求流程分析
到这里,我们已经完成了一个完整的 Ajax 通信案例。
但是对于初学者来说,最容易混乱的地方在于:
到底是谁先执行?
谁在等待?
数据又是怎么回来的?
下面我们从用户打开页面开始,梳理整个请求流程。
26.1 页面加载
浏览器加载:
index.html
开始执行 JavaScript:
console.log("start");
输出:
start
接着注册按钮点击事件:
document
.getElementById("btn")
.addEventListener(...)
此时:
事件注册完成
↓
等待用户点击
但并不会立即执行回调函数。
26.2 创建 XMLHttpRequest 对象
执行:
const xhr = new XMLHttpRequest();
浏览器创建一个 HTTP 请求对象。
此时:
readyState = 0
26.3 配置请求
执行:
xhr.open(
"GET",
"http://localhost:3000/todos",
true
);
请求配置完成。
状态变为:
readyState = 1
26.4 注册状态监听
执行:
xhr.onreadystatechange = function(){
}
注册回调函数。
此时浏览器开始监听请求状态变化。
26.5 发送请求
执行:
xhr.send();
浏览器正式向服务器发送请求。
注意:
发送请求
≠
等待响应
请求发送后。
JavaScript 会继续向下执行:
console.log("end");
输出:
end
26.6 Node服务器接收请求
服务器收到:
GET /todos
请求。
进入:
if(req.url === "/todos")
对应逻辑。
然后返回:
JSON.stringify(todos)
生成的 JSON 字符串。
26.7 浏览器收到响应
收到响应后:
onreadystatechange
被触发。
状态依次变化:
2
↓
3
↓
4
当:
xhr.readyState === 4
并且:
xhr.status === 200
说明请求成功完成。
26.8 解析 JSON 数据
执行:
JSON.parse(
xhr.responseText
)
完成反序列化。
转换过程:
JSON字符串
↓
JavaScript对象
26.9 更新页面
执行:
innerHTML = ...
更新 DOM。
浏览器重新渲染页面。
最终显示:
过四六级
回家过节
二十七、为什么 Ajax 能实现局部刷新
很多初学者会产生一个疑问:
为什么 Ajax 请求不会刷新整个页面?
原因在于:
Ajax 请求获取的是:
数据
而不是:
新的HTML页面
传统网页:
请求
↓
返回HTML
↓
重新渲染整个页面
Ajax:
请求
↓
返回JSON
↓
JavaScript处理数据
↓
更新部分DOM
因此:
页面无需重新加载
这就是局部刷新的本质。
二十八、Fetch:现代 Ajax 方案
虽然 XMLHttpRequest 仍然能够工作。
但现代开发更推荐:
fetch()
例如:
fetch(
"http://localhost:3000/todos"
)
.then(res => res.json())
.then(data => {
console.log(data);
});
代码更加简洁。
逻辑也更加清晰。
Fetch 与 XMLHttpRequest 对比
XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open(...);
xhr.onreadystatechange = function(){
};
xhr.send();
Fetch
fetch(url)
.then(...)
.then(...)
明显更加简单。
二十九、Async 与 Await
现代项目中最推荐的写法是:
async/await
例如:
async function getTodos(){
const res =
await fetch(
"http://localhost:3000/todos"
);
const data =
await res.json();
console.log(data);
}
相比 Promise:
fetch(...)
.then(...)
.then(...)
.then(...)
Async/Await 具有以下优势:
- 更接近同步代码
- 可读性更高
- 更容易维护
- 错误处理更统一
因此已经成为当前前端开发主流方案。
总结
本文通过一个完整的 TodoList 案例,系统学习了 Ajax 的核心知识。
主要内容包括:
- Ajax 的本质与作用
- JavaScript 单线程模型
- Event Loop 事件循环
- JSON.stringify 与 JSON.parse
- Node.js HTTP 服务
- 路由机制
- CORS 跨域问题
- XMLHttpRequest 工作原理
- readyState 状态变化
- HTTP 状态码
- DOM 动态渲染
- Fetch 与 Async/Await
通过这个案例,我们实际上已经完成了一次完整的前后端通信实践:
浏览器
↓
Ajax请求
↓
Node服务器
↓
JSON数据
↓
浏览器解析
↓
DOM渲染
↓
页面更新
理解这一套流程之后,再学习 Express、数据库、RESTful API、Vue、React 等技术时,就能够更加清晰地理解它们背后的运行原理。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)