AJAX 3------原理
1.XMLHttpRequest
-
定义:XHR用于与服务器交互,通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。
-
axios与XHR关系:axios使用XMLHttpRequest与服务器沟通
-
使用XMLHttpRequest
- 创建XMLHttpRequest对象
- 配置请求方法和URL网址
- 监听loadend事件,接收响应结果
- 发起请求
js// 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 obj = JSON.parse(xhr.response) console.log(obj) const str = obj.list.join('<br>') document.querySelector('p').innerHTML = str }) // 4. 发起请求 xhr.send()
-
XMLHttpRequest------查询参数
- http://xxxx.com/xxx/xxx?参数名1=值1\&参数名2=值2
- axios写到params对象中,XMLHttpRequest需要在配置url时跟到后面
- 注意下面案例中形成参数串的方法
jsconst pname = document.querySelector('[name=province]').value const cname = document.querySelector('[name=city]').value //组织查询字符串 防止参数过多一个一个添加太麻烦 const qObj = { pname, cname } const paramsObj = new URLSearchParams(qObj) const queryString = paramsObj.toString() console.log(queryString); //使用xhr const xhr = new XMLHttpRequest() xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`) xhr.addEventListener('loadend', () => { console.log(xhr.response) const obj = JSON.parse(xhr.response) console.log(obj) const str = obj.list.map((item) => { return ` <li class="list-group-item">${item}</li> ` }).join('') document.querySelector('.list-group').innerHTML = str }) xhr.send()
-
XMLHttpRequest------提交数据(通过请求体)
jsdocument.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') //准备数据并转成JSON字符串 const user = { username: 'lyq123456', password: '123456' } const userStr = JSON.stringify(user) xhr.send(userStr) })
2.Promise
- 定义:Promise对象用于管理一个异步操作最终状态和结果值
- axios与Promise关系:axios利用Promise管理异步通信的状态和结果
- 好处 :
- 逻辑更清晰
- 了解axios函数内部运作机制
- 解决回调函数地狱问题
- 语法
js
//1.创建Promise对象
const p = new Promise((resolve, reject) => {
//2.执行异步代码 成功用resolve,失败用reject
setTimeout(() => {
// resolve('模拟AJAX请求-成功结果')
reject(new Error('模拟AJAX请求-失败结果'))
}, 2000)
})
//3.获取结果
p.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
-
三种状态:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):操作成功,调用resolve()后,触发.then()
- 已拒绝(rejected) :操作失败,调用reject()后,触发.catch()
-
使用Promise管理XHR请求省份列表
js
<p class="my-p"></p>
<script>
/**
* 目标:使用Promise管理XHR请求省份列表
* 1. 创建Promise对象
* 2. 执行XHR异步代码,获取省份列表
* 3. 关联成功或失败函数,做后续处理
*/
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
//判断成功还是失败------通过状态码
// 2XX开头都是成功
console.log(xhr)
console.log(xhr.response)
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
p.then(result => {
console.log(result)
const str = result.list.join('<br>')
document.querySelector('.my-p').innerHTML = str
}).catch(error => {
//错误对象要用console.dir详细打印
console.dir(error)
document.querySelector('.my-p').innerHTML = error.message
})
</script>
-
若有查询参数,添加以下代码
jsif (config.params) { //组织查询字符串 const paramsObj = new URLSearchParams(config.params) const queryString = paramsObj.toString() //把查询参数字符串接在url?后面 config.url += `?${queryString}` }
-
若需要传递请求体数据,添加以下代码:
jsif (config.data) { const jsonStr = JSON.stringify(config.data) //告诉服务器,我传递的内容类型,是JSON字符串 xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(jsonStr) } else { xhr.send() }
3.使用XMLHttpRequest+Promise封装一个简易的axios函数
js
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))
}
})
if (config.data) {
const jsonStr = JSON.stringify(config.data)
//告诉服务器,我传递的内容类型,是JSON字符串
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
xhr.send()
}
})
}
4.回调函数地狱及Promise链式调用
- 回调函数地狱:在回调函数中嵌套回调函数,
- 缺点:可读性差,里面异常无法捕获,耦合性严重
js
// 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链式调用
- 定义:依靠then()方法会返回一个新生成的Promise对象特性,继续串联下一环任务
- 注意then()回调函数中的返回值会影响新生成的Promise对象最终状态和结果
- 作用:解决回调函数地狱问题
eg:Promise链式调用:
js
/**
* 目标:掌握Promise的链式调用
* 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
//1.创建省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
//2.获取省份名字
const p2 = p.then(result => {
console.log(result)
//创建Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '---北京')
}, 2000)
})
})
//3.获取城市名字
p2.then(result => {
console.log(result)
})
//then()原地的结果是一个新的Promise对象
console.log(p2 === p) //false
eg:使用Promise链式调用解决回调函数地狱问题
js
<body>
<form>
<span>省份:</span>
<select>
<option class="province"></option>
</select>
<span>城市:</span>
<select>
<option class="city"></option>
</select>
<span>地区:</span>
<select>
<option class="area"></option>
</select>
</form>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:把回调函数嵌套代码,改成Promise链式调用结构
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
let pname = ''
//获取省份的promise对象
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
console.log(result);
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
return axios({
url: 'http://hmajax.itheima.net/api/city', params: { pname }
})
}).then(result => {
console.log(result);
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
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
})
</script>
</body>
5.async函数和await
-
定义:async是使用async关键字声明的函数,是AsyncFunction构造函数的实例。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,无需刻意的链式调用promise
-
使用async函数和await解决回调函数地狱
-
在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
-
await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
js
<script>
/**
* 目标:掌握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 areaObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
const areaName = areaObj.data.list[0]
document.querySelector('.area').innerHTML = areaName
document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
}
getData()
</script>
6.宏任务和微任务(异步任务分类)
-
宏任务:由浏览器环境执行的异步代码,eg:JS脚本执行事件,setTimeout/setInterval、AJAX请求完成事件,用户交互事件
-
微任务:由JS引擎环境执行的异步代码,eg:Promise对象.then()'
-
代码执行顺序:优先调度微任务队列,再调度宏任务队列
7.Promise.all静态方法
- 定义:合并多个Promise对象,等待所有同时成功完成,做后续逻辑
js
const bj = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
const sh = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
const gz = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
const sx = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })
const p = Promise.all(bj, sh, gz, sx)
p.then(result => {
//对返回的数组结果进行操作
console.log(result)
}).catch(error => {
console.dir(error)
})