XMLHttpRequest
定义
- XMLHttpRequest(XHR))对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用。
- axios 内部采用 XMLHttpRequest 与服务器交互
基本使用
- 创建
XMLHttpRequest
对象 - 配置请求方法和请求 url 地址
- 监听
loadend
事件,接收响应结果 - 发起请求
示例代码如下:
// 1.创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
// 2.配置请求方法和请求 url 地址
xhr.open('get', 'http://hmajax.itheima.net/api/province')
// 3.监听 loadend 事件,接收响应结果
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
const data = JSON.parse(xhr.response) // 手动转为对象
console.log(data)
})
// 4.发起请求
xhr.send()
查询参数
请求头
使用 URLSearchParams
格式化对象为 get 请求参数,示例代码如下:
// 组织查询参数字符串
const qObj = {
pname: '浙江省',
cname: '杭州市'
}
// 查询参数对象 -> 查询参数字符串
const paramsObj = new URLSearchParams(qObj)
const queryString = paramsObj.toString()
console.log(queryString) // pname=&cname=
// 使用XHR对象,查询地区列表
const xhr = new XMLHttpRequest()
xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
xhr.send()
请求体
通过 setRequestHeader
方法设置请求头(Content-Type)表示请求体的内容类型,示例代码如下:
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 对象用于表示一个异步操作的最终完成(或失败)及其结果值
语法
// 1.创建 Promise 对象
const p = new Promise( (resolve, reject) => {
// 2.执行异步代码
setTimeout(() => {
resolve('模拟 Ajax 请求成功结果')
// reject(new Error('模拟 Ajax 请求失败结果'))
}, 2000)
})
// 3.获取结果
p.then(result => {
console.log(result) // 模拟 Ajax 请求成功结果
}).catch(error => {
console.log(error)
})
三种状态
一个 Promise 对象,必然处于以下几种状态之一:
- 待定( pending ) :初始状态,既没有被兑现,也没有被拒绝
- 已兑现( fulfilled ):操作成功完成
- 已拒绝( rejected ):操作失败
Promise对象一旦被 兑现 / 拒绝 就是已敲定了,状态无法再被改变
![在这里插入图片描述](https://img-blog.csdnimg.cn/51a25714bffd4590a7bbb68340cd6de7.png)
封装简易版 axios
基于 Promise + XMLHttpRequest 封装 myAxios 函数,示例代码如下:
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
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))
}
})
// 1. 判断有data选项,携带请求体
if (config.data) {
// 2. 转换数据类型,在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', () => {
// 3. 使用myAxios函数,完成注册用户
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: '校花6666666',
password: '12345678'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})
Promise.all
Promise.all
静态方法用于合并多个 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'}})
// 合并多个 Promise 对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
// 返回结果数组的顺序和合并时顺序一致
console.log(result)
}).catch(error => {
console.dir(error)
})
解决回调函数地狱
回调函数地狱
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重
示例代码如下:
// 1. 获取默认第一个省份的名字
axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
const pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2. 获取默认第一个城市的名字
axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3. 获取默认第一个地区的名字
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
})
})
}).catch(error => {
console.dir(error)
})
Promise - 链式调用
axios 函数底层基于 Promise,Promise 的 then()
方法会返回一个新生成的 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 new axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
}
).then(
result => {
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
}
)
async 和 await
axios
返回的是一个Promise
对象,返回的是处理成功后的结果then
方法也是返回的也是一个Promise
对象,作用见 👆 的链式调用代码
- 概念:在
async
函数内,使用await
关键字取代then
函数,等待获取Promise
对象成功状态的结果值 await
会阻止async
异步函数的继续执行,原地等待结果- 使用
try...catch
捕获异常
示例代码如下:
// 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()
同步代码和异步代码
- 同步代码:逐行执行,原地等待结果后,才继续向下执行
- 异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数
JS 中的异步代码:
- setTimeout / setInterval
- 事件
- AJAX
事件循环
由于 js 是单线程的,为了不阻塞 js 引擎,js 引入了事件循环机制,步骤如下:
- 执行同步代码,遇到异步代码交给宿主浏览器环境执行
- 异步有了结果后,把回调函数放入任务队列排队
- 当调用栈空闲后,反复调用任务队列里的回调函数