异步的JavaScript和XML
[Asynchronous JavaScript And XML]XMLHttpRequest
对象与服务器通信axios
库,与服务器进行数据交互XMLHttpRequest
对象的使用,了解 AJAX的底层原理语法:
1.引入 axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
2.使用 axios 函数:
2.1 传入配置对象
2.2 使用 .then 回调函数接收结果,并做后续处理
<body>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
axios({
url: 'http://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/csv/100000_province.json',
}).then((result) => { console.log(result.data.rows) })
script>
body>
<body>
<ul>ul>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
axios({
url: 'https://hmajax.itheima.net/api/city',
method: 'get',
params: {
pname: prompt('请输入省份:')
},
}).then((result) => {
// 筛选有效的数据
cityArr = result.data.list
// 将数据展示到页面上
const str = cityArr.map(ele => `${ele}`).join('')
document.querySelector('ul').innerHTML = str
})
script>
body>
<body>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
axios({
url: 'https://hmajax.itheima.net/api/register',
method: 'post',
// 要提交的数据
data: {
username: prompt('请输入注册账号'),
password: prompt('请输入密码')
}
}).then((result) => {
alert(result.data.message)
})
script>
body>
<script>
axios({
//请求选项
}).then(() => {
// 处理数据
}).catch(error => {
// 处理错误
})
script>
<body>
<div>
<label>注册账号:<input type="text">label><br>
<label>填写密码:<input type="password">label>
div>
<button>注册button>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
const input = document.querySelector('input[type=text]')
const password = document.querySelector('input[type=password]')
const button = document.querySelector('button')
button.addEventListener('click', () => {
axios({
url: 'https://hmajax.itheima.net/api/register',
method: 'post',
// 要提交的数据
data: {
username: input.value,
password: password.value
}
}).then((result) => {
alert(result.data.message)
}).catch(error => {
alert(error.response.data.message)
})
})
script>
body>
XHR
即 XMLHttpRequest
XMLHttpRequest
对象用于与服务器交互XMLHttpRequest
可以在不刷新页面的情况下,请求特定URL,获取数据XMLHttpRequest
在 AJAX 编程中被大量使用XMLHttpRequest
与服务器交互<body>
<ul>ul>
<script>
// 1. 创建XMLHttpRequset对象
const xhr = new XMLHttpRequest()
// 2. 配置请求方法和URL地址
xhr.open('GET', 'https://hmajax.itheima.net/api/province')
// 3. 提前准备好一个监听loadend事件,用来接收响应结果
xhr.addEventListener('loadend', () => {
// 数据处理与筛选
const result = JSON.parse(xhr.response)
const arr = result.list
// 将数据渲染到页面
document.querySelector('ul').innerHTML =
arr.map(ele => `${ele}`).join('')
})
// 4.向服务端发送请求
xhr.send()
script>
body>
<body>
<ul>ul>
<script>
// 目标:使用XHR携带查询参数
const xhr = new XMLHttpRequest()
const goal = prompt('您要搜索的城市:')
xhr.open('GET', `https://hmajax.itheima.net/api/city?pname=${goal}`)
xhr.addEventListener('loadend', () => {
const result = JSON.parse(xhr.response
const arr = result.list
// 将数据渲染到页面
document.querySelector('ul').innerHTML =
arr.map(ele => `${ele}`).join('')
})
xhr.send()
script>
body>
<body>
<button class="reg-btn">注册用户button>
<script>
// 目标:使用XHR实现数据提交,完成注册功能
document.querySelector('.reg-btn').addEventListener('click', () => {
const xhr = new XMLHttpRequest()
xhr.open('POST', 'https://hmajax.itheima.net/api/register')
xhr.addEventListener('loadend', () => {
const result = JSON.parse(xhr.response)
console.log(result)
})
// xhr设置请求头:告诉服务器内容类型(JSON字符串)
xhr.setRequestHeader('Content-Type', 'application/json')
// 准备要提交的数据
const dataObj = {
username: 'jackJack',
password: '123456'
}
const dataStr = JSON.stringify(dataObj) // 将数据对象转为JSON字符串
// 设置好"请求体",并发送请求
xhr.send(dataStr)
})
script>
body>
Promise
对象用于表示一个异步操作的最终完成(或失败)的结果
<script>
// 1.创建Promise对象
const p = new Promise((resolve, reject) => {
// 2.执行异步任务
// 任务执行成功,就调用:resolve(...) 从而触发 then() 执行
// 任务执行失败,就调用:reject(...) 从而触发 catch() 执行
})
// 3.接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})
script>
Promise
对象有三种状态
<script>
// 1.创建Promise对象(此时Promise对象处于pending状态,即:待定状态)
const p = new Promise((resolve, reject) => {
// Promise对象创建时,这里的代码都会执行了
console.log('是这里先执行吗?');
setTimeout(() => {
resolve('模拟AJAX请求:结果成功!') // 当执行完resolve(),此时Promise对象处于fulfilled状态,即:已兑现状态
// reject(new Error('模拟AJAX请求:结果失败!')) // 当执行完reject(),此时Promise对象处于rejected状态,即:已拒绝状态
}, 2000)
})
console.log('还是说,这里先执行呢?')
// 3.获取结果
p.then(result => {
console.log(result);
}).catch(error => {
console.log(error);
})
script>
需求:使用 Promise + XHR 获取省份列表
<body>
<ul>ul>
<script>
// 1. 创建Promise对象
const p = new Promise((resolve, reject) => {
// 2. 执行XHR异步代码,获取省份列表
const xhr = new XMLHttpRequest()
xhr.open('GET', 'https://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
// console.log(xhr.status) // 响应状态码
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
// 正常要执行的代码(非异步的)
console.log('假设这里')
console.log('有着很多很多的')
console.log('代码需要执行')
console.log('-------------')
// 3. 返回结果
p.then(result => {
// 先给用户提前告知一声结果
alert('省份数据获取成功!')
// 然后将数据渲染到页面上
arr = result.list
document.querySelector('ul').innerHTML =
arr.map(ele => `${ele}`).join('')
}).catch(error => {
// 先给用户提前告知一声结果
alert('省份数据获取失败!')
// 错误对象要用console.dir详细打印
console.dir(error)
// 并且将关键的错误信息,显示到页面给用户看
document.querySelector('ul').innerHTML = `${error.message}`
})
// 正常要执行的代码(非异步的)
console.log('假设这里')
console.log('有着很多很多的')
console.log('代码需要执行')
console.log('-------------')
script>
body>
概念:在回调函数中嵌套回调函数,一直嵌套下去形成了回调函数地狱
缺点:
例子:如下
<body>
<div>
<div class="province">div>
<div class="city">div>
<div class="area">div>
div>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
// 获取省份
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: pname } }).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 获取地区
axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: pname, cname: cname } }).then(result => {
const area = result.data.list[0]
document.querySelector('.area').innerHTML = area
})
})
})
script>
body>
一个 Promise对象
在它的 then() 方法
里可以 return
一个 新的 Promise对象
利用好这个特性,就能串联下一个任务,实现链式调用
<body>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
const p1 = new Promise((resolve, reject) => {
// 模拟请求
setTimeout(() => {
resolve('湖北省') // 2秒之后服务器成功返回了数据:"湖北省",通过resolve()传递给then()
}, 2000)
})
const p2 = p1.then(result => {
console.log(result) // 输出传递过来的结果
return new Promise((resolve, reject) => {
// 模拟请求
setTimeout(() => {
resolve('武汉市') // 2秒之后服务器成功返回了数据:"武汉市",通过resolve()传递给then()
}, 2000)
})
})
const p3 = p2.then(result => {
console.log(result) // 输出传递过来的结果
return new Promise((resolve, reject) => {
// 模拟请求
setTimeout(() => {
resolve('武昌区') // 2秒之后服务器成功返回了数据:"武昌区",通过resolve()传递给then()
}, 2000)
})
})
p3.then(result => {
console.log(result) // 输出传递过来的结果
})
script>
body>
<body>
<ul>ul>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
// 防止变量污染
(function () {
// 定义变量,用来存储数据
let province
let city
let area
// 利用"链式调用"解决"回调函数地狱"
let obj = axios({ url: 'http://hmajax.itheima.net/api/province' }) // 请求获取省份数据
obj = obj.then(result => {
console.log(result) // 输出省份数据
province = result.data.list[18]
return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } }) // 请求获取城市数据
})
obj = obj.then(result => {
console.log(result) // 输出城市数据
city = result.data.list[0]
return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } }) // 请求获取地区数据
})
obj.then(result => {
console.log(result) // 输出地区数据
area = result.data.list[0]
// 将数据显示到页面上
document.querySelector('ul').innerHTML = `${province}${city}${area}`
})
})()
script>
body>
概念:合并多个Promise对象,等待所有的Promise对象完成,才进行后续逻辑
语法:如下
<script>
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result结果: [Promise对象成功的结果, Promise对象成功的结果, ...]
}).catch(error => {
// 最先失败的Promise对象,抛出的异常
})
script>
<body>
<ul>ul>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
// 目标:实现将北京、上海、广州、深圳的天气同时展示到页面上
// 1. 发起请求,得到Promise对象
const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } }) // 北京Promise对象
const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } }) // 上海Promise对象
const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } }) // 广州Promise对象
const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } }) // 深圳Promise对象
// 2. 使用Promise.all,合并多个Promise对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
console.log(result)
// 将数据渲染到页面上
document.querySelector('ul').innerHTML =
result.map(ele => {
const info = ele.data.data
return `[地区: ${info.area}]-[时间: ${info.date}]-[天气: ${info.weather}]`
}).join('')
}).catch(error => {
console.dir(error)
})
script>
body>
需求:基于 Promise + XHR 封装 myAxios 函数
<body>
<ul>ul>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
axios({
url: 'https://hmajax.itheima.net/api/province',
method: 'get'
}).then(result => {
// console.log(result) // 返回数据对象
// console.log(result.data.list) // 筛选有效信息
// 将数据渲染到页面
const arr = result.list
document.querySelector('ul').innerHTML =
arr.map(ele => `${ele}`).join('')
}).catch(error => {
console.log(error)
})
script>
body>
<body>
<script>
function myAxios(configObj) {
// ------------函数体--------------
// 函数第一步:创建Promise对象
const p = new Promise((resolve, reject) => {
// ----------------异步代码---------------------
// 1.1 判断configObj是否有params属性
if (configObj.params) {
// 1.2 使用URLSearchParams将"查询参数的对象"转换成"符合查询参数格式的字符串"
const paramsObj = new URLSearchParams(configObj.params)
const queryString = paramsObj.toString()
// 1.3 把查询参数字符串,拼接到URL后面
configObj.url += `?${queryString}`
}
// 2.1 准备好一个用于与服务器连接的xhr对象
const xhr = new XMLHttpRequest()
// 2.2 给xhr配置相关信息
xhr.open(configObj.method, configObj.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr))
}
})
// 3.1 判断是否有data选项
if (configObj.data) {
// 3.2 转换数据类型,准备好"数据体"
const jsonStr = JSON.stringify(configObj.data)
// 3.3 配置好"数据头"
xhr.setRequestHeader('Content-Type', 'application/json')
// 3.4 携带数据,向服务器发送"请求"
xhr.send(jsonStr)
} else {
// 3.5 如果没有data选项,直接发送"请求"即可
xhr.send()
}
// ----------------异步代码---------------------
})
// 函数第二步:返回Promise对象
return p
// ------------函数体--------------
}
script>
body>
为了方便日后使用简易版的 axios,现将它简化压缩成下面代码(用法不变)
function simpleAxios(config) {
return new Promise((resolve, reject) => {
config.params && (config.url += `?${new URLSearchParams(config.params).toString()}`)
const xhr = new XMLHttpRequest()
xhr.open(config.method, 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) {
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(JSON.stringify(config.data))
} else { xhr.send() }
})
}
<body>
<div>
<div class="province">div>
<div class="city">div>
<div class="area">div>
div>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
// 1. 定义 async 修饰函数
async function getData() {
// 1.1. await 等待 Promise 对象成功的结果
// await 必须用在 async 修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
const province = pObj.data.list[0]
const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } })
const city = cObj.data.list[0]
const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } })
const area = aObj.data.list[0]
// 1.2 将数据渲染到页面上
document.querySelector('.province').innerHTML = province
document.querySelector('.city').innerHTML = city
document.querySelector('.area').innerHTML = area
}
// 2. 调用函数
getData()
script>
body>
<body>
<div>
<div class="province">div>
<div class="city">div>
<div class="area">div>
div>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
async function getData() {
try {
const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
const province = pObj.data.list[0]
const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } })
const city = cObj.data.list[0]
const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } })
const area = aObj.data.list[0]
document.querySelector('.province').innerHTML = province
document.querySelector('.city').innerHTML = city
document.querySelector('.area').innerHTML = area
} catch (error) {
console.dir(error)
}
}
// 调用函数
getData()
script>
body>