Ajax、Promise、axios基础笔记
摘要:本文介绍了AJAX技术的核心概念和使用方法,重点讲解了XMLHttpRequest对象、axios库的应用、Promise异步编程、async/await语法以及事件循环机制。内容涵盖GET/POST请求、URL参数处理、HTTP协议报文、回调地狱解决方案、链式调用、微任务宏任务区分等关键技术点,并提供了封装简易axios函数和Promise.all批量处理的实战示例,帮助开发者掌握现代前端
AJAX
-
使用浏览器的 XMLHttpRequest 对象 与服务器通信,动态数据交互
-
浏览器网页中,使用 AJAX技术(XHR对象)发起获取数据的请求,服务器代码响应准备好的数据给前端,前端拿到数据数组以后,展示到网页
-
学习AJAX这里使用 第三方库axios,axios库语法简单

axios使用

-
引入 axios.js
axios.js文件链接: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
学习vue这些框架后可以用npm包管理器来安装axios
-
axios函数的使用语法
axios({ url: '目标资源地址' }).then((result) => { // 对服务器返回的数据做后续处理 })注意:请求的 url 地址, 就是标记资源的网址
URL
统一资源定位符,简称网址,用于定位网络中的资源(资源指的是:网页,图片,数据,视频,音频等等)
组成:协议,域名,资源路径(URL 组成有很多部分,我们先掌握这3个重要的部分即可)
-
协议:超文本传输协议,规定了浏览器和服务器传递数据的格式
-
域名:标记服务器在互联网当中的方位,网络中有很多服务器,你想访问哪一台,就需要知道它的域名才可以
-
资源路径:一个服务器内有多个资源,用于标识你要访问的资源具体的位置

url查询参数
携带给服务器额外信息,让服务器返回我想要的某一部分数据而不是全部数据
在 url 网址后面用?拼接格式:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
参数名一般是后端规定的,值前端看情况传递即可
常用请求方法和数据提交
请求方法:GET,POST,PUT,DELETE,PATCH(这些都是http协议规定的),每个单词对应一种对服务器资源要执行的操作
常用的 get 和 post

// get请求 获取数据 默认 可不写method,传参params
axios({
url: '目标资源地址',
params: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
})
// post请求 提交数据 method里写请求方法,data写参数
axios({
url: '目标资源地址',
method: 'POST',
data: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
})
// 错误处理 catch捕获这次请求响应的错误并做后续处理
axios({
// ...请求选项
}).then(result => {
// 处理成功数据
}).catch(error => {
// 处理失败错误
console.log(error)
console.log(error.response.data.message)
alert(error.response.data.message)
})
HTTP 协议-请求报文
HTTP 协议规定了浏览器和服务器返回内容的格式
请求报文:是浏览器按照协议规定发送给服务器的内容
这里的格式包含:
-
请求行:请求方法,URL,协议
-
请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
-
空行:分割请求头,空行之后的是发送给服务器的资源
-
请求体:发送的资源
HTTP 协议-响应报文
响应报文:是服务器按照协议固定的格式,返回给浏览器的内容
响应报文的组成:
-
响应行(状态行):协议,HTTP响应状态码,状态信息
-
响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
-
空行:分割响应头,控制之后的是服务器返回的资源
-
响应体:返回的资源
HTTP 响应状态码:
-
用来表明请求是否成功完成
-
例如:404(客户端要找的资源,在服务器上不存在)

XMLHttpRequest - 基础使用
axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数
XHR语法如下:
// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
// 2. 配置请求方法和请求 url 地址
xhr.open('请求方法', '请求url网址')
// 3. 监听 loadend 事件,接收响应结果
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.response)
})
// 4. 发起请求
xhr.send()
XMLHttpRequest - 查询参数
原生 XHR 需要自己在 url 后面携带查询参数字符串,没有 axios 帮助我们把 params 参数拼接到 url 字符串后面了
但是多个查询参数,如果自己拼接很麻烦,这里用 URLSearchParams把参数对象转成“参数名=值&参数名=值“格式的字符串,语法如下:
// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
参数名1: 值1,
参数名2: 值2
})
// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2
XMLHttpRequest- 数据提交
-
需要自己设置请求头
Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用 -
需要前端自己把 JS 对象转成 JSON 字符串
-
原生 XHR 需要在 send 方法调用时,传入请求体携带
核心代码如下:
/**
* 目标:使用xhr进行数据提交-完成注册功能
*/
document.querySelector('.reg-btn').addEventListener('click', () => {
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://hmajax.itheima.net/api/register')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
// 设置请求头-告诉服务器内容类型(JSON字符串)
xhr.setRequestHeader('Content-Type', 'application/json')
// 准备提交的数据
const userObj = {
username: 'itheima007',
password: '7654321'
}
const userStr = JSON.stringify(userObj)
// 设置请求体,发起请求
xhr.send(userStr)
})
Promise
Promise 对象用于表示一个异步操作的最终完成(或失败)及其结构值
Promise 管理异步任务,语法怎么用?
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 执行异步任务-并传递结果
// 成功调用: resolve(值) 触发 then() 执行
// 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})
Promise 的状态
每个 Promise 对象必定处于以下三种状态之一
-
待定(pending):初始状态,既没有被兑现,也没有被拒绝
-
已兑现(fulfilled):操作成功完成
-
已拒绝(rejected):操作失败
每个 Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变

/**
* 目标:使用Promise管理XHR请求省份列表
* 1. 创建Promise对象
* 2. 执行XHR异步代码,获取省份列表
* 3. 关联成功或失败函数,做后续处理
*/
// 1. 创建Promise对象
const p = new Promise((resolve, reject) => {
// 2. 执行XHR异步代码,获取省份列表
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
// xhr如何判断响应成功还是失败的?
// 2xx开头的都是成功响应状态码
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
// 3. 关联成功或失败函数,做后续处理
p.then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
// 错误对象要用console.dir详细打印
console.dir(error)
// 服务器返回错误提示消息,插入到p标签显示
document.querySelector('.my-p').innerHTML = error.message
})
封装_简易axios
基于 Promise 和 XHR 封装 myAxios 函数
核心代码:
// 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
return new Promise((resolve, reject) => {
// 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
// 判断有params选项,携带查询参数
if (config.params) {
// 使用URLSearchParams转换,并携带到url上
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
// 把查询参数字符串,拼接在url?后面
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
// 调用成功/失败的处理程序
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
// 判断有data选项,携带请求体
if (config.data) {
// 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
// 如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}
// 使用
document.querySelector('.reg-btn').addEventListener('click', () => {
// 使用myAxios函数,完成注册用户
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima999',
password: '666666'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})
同步代码和异步代码
-
同步代码:逐行执行,需原地等待结果后,才继续向下执行
-
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果
回调函数地狱
在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱;可读性差,异常无法捕获,耦合性严重,牵一发动全身
axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
const pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 获取第一个省份默认下属的第一个城市名字
axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 获取第一个城市默认下属第一个地区名字
axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => {
document.querySelector('.area').innerHTML = result.data.list[0]
})
})
})
Promise-链式调用
-
概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
-
细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
-
好处:通过链式调用,解决回调函数嵌套问题
/**
* 目标:掌握Promise的链式调用
* 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
// 1. 创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
// 2. 获取省份名字
const p2 = p.then(result => {
console.log(result)
// 3. 创建Promise对象-模拟请求城市名字
// return Promise对象最终状态和结果,影响到新的Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '--- 北京')
}, 2000)
})
})
// 4. 获取城市名字
p2.then(result => {
console.log(result)
})
// then()原地的结果是一个新的Promise对象
console.log(p2 === p)
-
目标:使用 Promise 链式调用,解决回调函数地狱问题
-
做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

-
按照图解思路,编写核心代码:
/** * 目标:把回调函数嵌套代码,改成Promise链式调用结构 * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中 */ let pname = '' // 1. 得到-获取省份Promise对象 axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => { pname = result.data.list[0] document.querySelector('.province').innerHTML = pname // 2. 得到-获取城市Promise对象 return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }}) }).then(result => { const cname = result.data.list[0] document.querySelector('.city').innerHTML = cname // 3. 得到-获取地区Promise对象 return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }}) }).then(result => { console.log(result) const areaName = result.data.list[0] document.querySelector('.area').innerHTML = areaName })
async 和 await 函数
-
概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
-
做法:使用 async 和 await 解决回调地狱问题
-
核心代码:
/** * 目标:掌握async和await语法,解决回调函数地狱 * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值 * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果) */ // 1. 定义async修饰函数 async function getData() { // 2. await等待Promise对象成功的结果 const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'}) const pname = pObj.data.list[0] const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }}) const cname = cObj.data.list[0] const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }}) const areaName = aObj.data.list[0] document.querySelector('.province').innerHTML = pname document.querySelector('.city').innerHTML = cname document.querySelector('.area').innerHTML = areaName } getData()使用 await 替代 then 的方法
事件循环
-
事件循环(
EventLoop):掌握后知道 JS 是如何安排和运行代码的 -
作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务
-
原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
-
概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
宏任务与微任务
-
ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务
-
异步任务划分为了
-
宏任务:由浏览器环境执行的异步代码
-
微任务:由 JS 引擎环境执行的异步代码
-
-
宏任务和微任务具体划分:
-
ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务
-
异步任务划分为了
-
宏任务:由浏览器环境执行的异步代码
-
微任务:由 JS 引擎环境执行的异步代码
-
-
宏任务和微任务具体划分:

-
注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!
下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队
总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系
Promise.all静态方法
概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
// 1. 请求城市天气,得到Promise对象
const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })
// 2. 使用Promise.all,合并多个Promise对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
// 注意:结果数组顺序和合并时顺序是一致
console.log(result)
const htmlStr = result.map(item => {
return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
}).join('')
document.querySelector('.my-ul').innerHTML = htmlStr
}).catch(error => {
console.dir(error)
})
更多推荐


所有评论(0)