07-Ajax&Promise&fetch

参考资料:
- 永不收费开放api(感谢大神无私奉献):https://api.aa1.cn/
- 今日热榜api:https://api.aa1.cn/doc/dailyhot.html
1. Ajax
1.1 背景:局部更新
Ajax(Async Javascript And Xml),异步HTTP请求,客户端给服务器发送消息的工具,以及接收响应的工具,默认异步执行的功能。
优势:
- 不需要插件的支持,原生js就可以使用
- 用户体验号(不需要刷新页面就可以更新数据)
- 减轻服务和带宽的负担
缺点:
- 搜索引擎的支持度不够(即SEO不友好),因为数据都不在页面上,搜索引擎搜索不到
AJAX 仅仅组合了:
- 浏览器内建的 XMLHttpRequest 对象(从 web 服务器请求数据)
- JavaScript 和 HTML DOM(显示或使用数据)

XMLHttpRequest 对象的三个重要的属性:
| 属性 | 描述 |
|---|---|
| onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。 |
| readyState | 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0: 请求未初始化,也就是open方法还没有执行1: 服务器连接已建立,即配置信息已经完成,也就是执行完open之后2: 请求已接收,表示send方法已经执行完成3: 请求处理中,表示正在解析响应内容4: 请求已完成,且响应已就绪,表示可以在客户端使用数据了 |
| status | 200: “OK” 404: 未找到页面 |
在 onreadystatechange 事件中,我们规定当服务器响应已做好被处理的准备时所执行的任务。
当 readyState 等于 4 且状态为 200 时,表示响应已就绪。
1.2 ajax语法
步骤:
let xhr = new XMLHttpRequest()创建XHR请求对象xhr.open(请求方式, 请求地址[, 是否异步])配置xhr.send()发送请求xhr.onreadystatechange = function() {... 4 && 200 ...}接收响应,注册一个事件- 或
xhr.onload = function() {...200...}onload默认readyState就是4,可以省略判断 /^2\d{2}$/.test(xhr.status)判断 200-299 都是成功的 HTTP 状态码
- 或
示例:
请求地址:http://localhost:5500/ajax/1.json
可以预先模拟后端响应的数据结构,因此可以将前端页面渲染工作前置。
1 | |
案例:ajax请求百度热榜
url地址:
1 | |
示例:
1 | |
效果:

1.3 ajax异步和同步
xhr.open(请求方式, 请求地址[, 是否异步]) 配置
- 第三个参数是否异步:
true-异步(默认),false-同步
注意:如果是同步时,需要在send前就要给 onload 绑定事件,否则send都响应结束了才绑定事件就晚了。
示例:
1 | |
1.4 请求方式
GET,明文传输,参数体现在URL上,有长度限制(太长会被截断)2KBPOST,请求体中传输,足够安全,请求体数据量可以比较大
Ajax下:
- GET,偏向于获取
- POST,偏向于提交
- PUT,偏向于更新(全部) - PATCH,偏向于更新(部分)
- DELETE,偏向于删除
准备:nodejs安装
nodejs 准备
- json-server,基于一个json文件就可以创建很多的后端模拟接口。
下载:https://nodejs.cn/download/
当前使用版本 v16.15.1 下载 .msi
当前电脑已安装版本(VSCode中
Alt + F12打开终端):
使用 npm 安装 json-server (-g 全局安装):
1
2npm install json-server -g
json-server --version
此时使用命令
json-server --version如果报错的话,则需要修改一下windows本机的脚本执行策略(允许执行脚本命令)
1
2Get-ExecutionPolicy //查看当前执行策略
Set-ExecutionPolicy RemoteSigned //修改执行策略
再次验证(已OK):
json-server支持的分页和详情接口请求方式:
模拟列表页:http://localhost:3000/api/?_page=1&_limit=10
模拟详情页:
准备一个 test.json 在当前代码路径下,打开终端监听 test.json(即启动好了后端):
test.json
1 | |
1 | |

浏览器查看:

浏览器查看api接口:

验证请求方式
预览使用插件 Preview on Web Server 可以
让 POST 请求不会去刷新页面。该插件的默认预览页面接口是 8080,通过快捷键进行启停。
快捷键改为了:
- Ctrl + Alt + Shift + R 启动服务
- Ctrl + Alt + Shift + L 预览页面
- Ctrl + Alt + Shift + S 停止服务
1 | |
1.5 ajax封装 util.js
util.js
1 | |
1 | |
验证ajax封装 util.js
1 | |
案例:模拟待办事项
使用 ajax封装+面向对象+json-server 功能。特别注意:同时移除li列表数据中指定的元素 的位置
test.json (使用nodejs的 json-server 进行启动:json-server .\test.json --watch )
1 | |
index.html
1 | |
效果:

2. Promise
2.1 背景:回调地狱
回调函数过多嵌套会导致 回调地狱,难以维护,一旦出错层层error。
示例:
1 | |
2.2 Promise语法
Promise 是一个语法,执行时都会有三个状态:
- pending,执行中
- fulfilled,
成功 - resolve() >> .then() - reject,
失败 - reject() >> .catch()
1 | |
.then() 方法可以嵌套多个,直到兑现承诺Promise。
2.3 Promise版封装ajax
封装新增的核心代码:
1 | |
基于封装后的使用:
1 | |
util.js - 基于 ajax封装 再次封装 promise
1 | |
2.4 Promise.all()聚合承诺
Promise.all([]) 可以同时执行多个 Promise,并且等它们全部成功后才返回结果。如果其中有一个失败,整个 Promise.all() 就会立刻 reject。
- 适用于并行任务,但不适合有依赖关系的任务。
示例:
1 | |
效果:

2.5 async与await语法
2.5.1 基本用法
参考资料:https://www.runoob.com/js/js-async-await.html
背景:
在 async/await 出现之前,JavaScript 主要使用回调函数处理异步操作,但这会导致”回调地狱”(Callback Hell),即多层回调嵌套结构使得代码难以阅读和维护。
ES6 引入了 Promise 对象来解决回调地狱问题。虽然 Promise 改善了回调问题,但 then() 链式调用仍然不够直观。
ES7(即ES2017) 引入了
async/await,它建立在 Promise 之上,让异步代码看/写起来像同步代码一样。
async 和 await 都是属于 ES7 支持的语法。
- async,在函数声明前添加,表示该函数是异步的。
- async 函数总是返回一个 Promise:
- 如果返回值不是 Promise,会自动包装成 resolved Promise
- 如果抛出异常,会返回 rejected Promise
- async 函数总是返回一个 Promise:
- await,只能在 async 函数内部使用,函数内部变同步执行。
- await 会暂停 async 函数的执行,等待 Promise 完成:
- 如果 Promise 被 resolve,返回 resolve 的值
- 如果 Promise 被 reject,抛出错误(可以用 try/catch 捕获)
- await 会暂停 async 函数的执行,等待 Promise 完成:
基本用法:
1 | |
错误处理:
1 | |
并行执行:
1 | |
2.5.2 最佳实践
- 不要忘记 await
1 | |
- 避免不必要的 async
1 | |
- 顶层await(在模块顶层可以直接使用 await(ES2022 特性))
1 | |
- 性能考虑
- 顺序执行 vs 并行执行:
合理使用 Promise.all 提高性能 - 错误处理:确保所有可能的错误都被捕获
- 顺序执行 vs 并行执行:
代码实例(一个接口依赖上一个接口的返回值如id的使用场景):
1 | |
输出:
1 | |
案例:模拟待办事项-async+await
1 | |
效果一模一样。
2.6 fetch
fetch 的出现是为了取代 XMLHttpRequest的,但不是为了取代 ajax的,它可以简洁地发起 HTTP 请求,并处理服务器的响应。
优点:
- 基于
Promises封装出来的,代码更加简洁和易读。 - 更好的错误处理机制:只在网络错误(如无法连接服务器)时返回
catch,而非状态码错误。 - 支持多种数据格式(JSON、文本、二进制等)。
- 可以处理跨域请求,通过
CORS机制配置。
缺点:
- 兼容性不太好,
不兼容 IE678,11,caniuse查询 - .catch 无法自动捕获错误,需要在第一个 .then 中手动去做拒绝承诺的返回
第一个 .then 固定返回的数据格式 Response:

fetch GET:
1 | |
fetch POST:
1 | |
案例:模拟待办事项-fetch
1 | |
效果一模一样。
案例:列表与详情交互
1. 接口分析
在实际开发中,后端通常会提供接口供前端调用。以下是一个示例接口的分析过程。
启动JSON Server
我们使用JSON Server来模拟后端接口。通过以下命令启动服务:
1 | |
启动后,服务会运行在http://localhost:3000。我们可以通过访问http://localhost:3000/goods来获取商品列表数据。
接口数据结构
访问接口后,返回的数据是一个数组,数组中的每个元素是一个商品对象。以下是一个示例数据结构:
db.json
1 | |
分页参数
为了实现懒加载功能,我们需要通过分页参数来获取数据。JSON Server支持以下分页参数:
_page:页码_pre_page:每页数据条数 (json-server 旧版本可以使用_limit)
例如,获取第一页的5条数据:
1 | |
正确的参数组合方案
根据源码实现,可以总结出三种安全的分页方式:
| 分页模式 | 参数组合 | 适用场景 |
|---|---|---|
| 页码分页 | _page=1&_per_page=10 |
用户界面分页控件 |
| 偏移分页 | _start=0&_limit=10 |
无限滚动加载 |
| 范围分页 | _start=0&_end=10 |
数据导出功能 |
注意:_per_page参数有默认值10,而_limit没有默认值。这意味着单独使用_page=2会返回10条数据,而单独使用_start=10会返回空数组。
页码分页的返回结果示例:
1 | |
1 | |
2. 懒加载实现
懒加载是一种常见的性能优化技术,通过分页加载数据,减少一次性加载的数据量,提升页面性能。
实现步骤
- 监听页面滚动事件:通过
scroll事件监听页面滚动。 - 判断是否滚动到底部:通过
scrollTop、scrollHeight和clientHeight判断是否滚动到底部。 - 发送AJAX请求:当滚动到底部时,发送AJAX请求获取下一页数据。
- 渲染数据:将获取到的数据渲染到页面中。
3. 跨域问题
在实际开发中,跨域问题是一个常见的挑战。跨域问题通常发生在前端请求的域名与后端接口的域名不一致时。
解决方案
- CORS:后端通过设置
Access-Control-Allow-Origin头来允许跨域请求。 - JSONP:通过
<script>标签加载数据,实现跨域请求。 - 代理服务器:通过代理服务器转发请求,避免跨域问题。
以下是一个使用CORS的示例:
1 | |
4. 列表跳转详情页面
在列表页面中,点击某个商品时,跳转到详情页面并显示该商品的详细信息。
实现步骤
- 传递参数:通过URL传递商品ID。
- 获取参数:在详情页面中通过URL获取商品ID。
- 发送请求:根据商品ID发送请求获取详细信息。
- 渲染页面:将获取到的详细信息渲染到页面中。
5. 常见问题及解答
以下是一些常见的问题及解答:
| 问题 | 答案 |
|---|---|
| 为什么懒加载功能不生效? | 检查是否正确监听了滚动事件,以及是否正确发送了分页请求。 |
| 跨域请求失败如何解决? | 确保后端设置了CORS头,或者使用JSONP或代理服务器。 |
| 详情页面如何获取商品ID? | 通过URL参数获取商品ID new URL(location.href).searchParams.get("id")。 |
| 如何实现分页功能? | 通过分页参数_page和_pre_page实现分页请求。 |
| 如何优化列表页面性能? | 使用懒加载技术,减少一次性加载的数据量。 |
6. 相似概念对比
以下是一些相似概念的对比:
| 概念 | 描述 | 适用场景 |
|---|---|---|
| AJAX | 异步请求技术,用于与服务器进行交互 | 实现页面局部更新,提升用户体验 |
| JSONP | 通过<script>标签加载数据,实现跨域请求 |
解决跨域问题,但仅支持GET请求 |
| CORS | 通过设置HTTP头允许跨域请求 | 解决跨域问题,支持多种请求方式 |
| 懒加载 | 分页加载数据,减少一次性加载的数据量 | 提升页面性能,优化用户体验 |
代码:

list.html
1 | |
detail.html
1 | |
js/list.js
1 | |
js/detail.js
1 | |
css/list.css
1 | |
css/detail.css
1 | |
效果:





