这是我们 Ajax 笔记的最后一章
async 和 await 被称为异步的终极解决方案
必须要学会使用
async 和 await 是一对关键字,需要配合使用
异步代码先不处理,先执行同步代码再执行异步操作。
测试:
整个代码分为两个宏任务,第一个宏任务是整个 script 标签代码,而延时函数属于第二个宏任务,
p 属于微任务,等第一个宏任务执行之后再执行 p。第一个宏任务和微任务结束之后,进行第二个宏任务,所以延时函数是最后输出的。
当我们 resolve 成功的时候,将会触发 Promise 的 then 方法,而 then 方法属于微任务,要等宏任务结束再进行。
技巧:先拆解出各个宏任务,然后按顺序拆解同一个宏任务下的微任务,先把微任务放一边,最后执行。
const p1 = () =>
new Promise((resolve, reject) => {
console.log(1)
let p2 = new Promise((resolve, reject) => {
console.log(2)
setTimeout(() => {
console.log(3)
resolve(4)
}, 0)
resolve(5)
})
resolve(6)
p2.then((arg) => {
console.log(arg);
})
})
setTimeout(() => {
console.log(8)
const p3 = new Promise((reject) => {
reject(9)
}).then((res) => {
console.log(res)
})
}, 0)
p1().then((arg) => {
console.log(arg)
})
console.log(10)
resolve(5) 对应的是 p2 的成功结果 then(),属于微任务
resolve(6) 对应的是第一个 Promise,成功结果输出 (arg)
坑点1(reject)解析:
Promise有两个参数(resolve, reject),第一个位置就是 resolve,代码中只不过是用 reject 命名了 resolve 这个参数,本质上还是 resolve。(这里是一个坑,面试要注意
)
坑点2(resolve(4))解析:
resolve(4) 作用是把 p2 这个 Promise 对象状态变成成功的,但是我们之前的 resolve(5) 已经转化了它的状态为成功,由于状态凝固的原因, resolve(4) 是无法生效的。因此不会打印数字结果 4。
实现保存功能这块,涉及到新知识
代码编写过程中:
出错原因是:发送请求的时候,少了一个接口文档要求的 avatar 数据
将头像路径存到隐藏域中的代码是放在头像更改函数中
的,但是头像没有发生变化,这个代码也就不会触发。所以隐藏域中没有 value 值,也就是头像的url地址。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./css/bootstrap.min.css" />
<style>
.form-select {
width: 103px;
display: inline-block;
}
.col-form-label {
text-align: right;
}
.figure-img {
width: 100px;
height: 100px;
cursor: pointer;
}
#upload {
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1 class="p-5">个人设置</h1>
<form class="col-6">
<div class="row mb-3">
<label class="col-form-label col-3">昵称:</label>
<div class="col-9">
<input class="form-control col-9" type="text" name="nickname" />
</div>
</div>
<div class="row mb-3">
<label class="col-form-label col-3">籍贯:</label>
<div class="col-9">
<select class="form-select col-4" name="province">
<option value="">--省--</option>
</select>
<select class="form-select col-4" name="city">
<option value="">--市--</option>
</select>
<select class="form-select col-4" name="area">
<option value="">--区--</option>
</select>
</div>
</div>
<div class="row mb-3">
<label class="col-form-label col-3">头像:</label>
<div class="col-9">
<input class="form-control col-9" type="hidden" name="avatar" />
<figure class="figure">
<input type="file" id="upload" />
<img src="https://yanxuan-item.nosdn.127.net/12a882699bd531a1bd428bffe1989525.jpg"
class="figure-img img-fluid rounded" id="myImg" alt="..." />
<figcaption class="figure-caption">修改头像</figcaption>
</figure>
</div>
</div>
<div class="row mb-3">
<label class="col-3"></label>
<div class="col-9">
<button class="btn btn-primary">保存</button>
</div>
</div>
</form>
</div>
<script src="./lib/bootstrap.min.js"></script>
<script src="./lib/axios.js"></script>
<script src="./lib/form-serialize.js"></script>
<script>
// 在线接口文档:https://www.showdoc.com.cn/1834761734600444/8477923164668939
// 配置请求根路径
axios.defaults.baseURL = 'http://ajax-api.itheima.net'
// 昵称
let nameInput = document.querySelector('[name=nickname]')
// 头像
let upload = document.querySelector('#upload')
let avatarImg = document.querySelector('#myImg')
// 隐藏域
let hiddenAvatar = document.querySelector('[name=avatar]')
// 省市区
let form = document.querySelector('form')
let provinceSelect = document.querySelector('[name=province]')
let citySelect = document.querySelector('[name=city]')
let areaSelect = document.querySelector('[name=area]')
// ========================================= 渲染展示功能 =========================================
// 获取数据结果,展示到页面中 // 不需要用 axios 和 then 的写法了
// 封装一个获取信息并渲染的函数
async function render() {
let { data: res} = await axios.get('/api/settings')
// 再继续对 res 对象进行解构
let {data: { area, avatar, city, nickname, province } } = res
// console.log(res)
// console.log(nickname)
// 昵称的值
nameInput.value = nickname
// 省 市 区
// 思路:先获取省市区的数据,再来进行设置
// 获取省数据(async 不用再写了 上面已经有 async 修饰过了)
let { data: resProvince } = await axios.get('/api/province')
// 把省的数据渲染展示到省的下拉框中
console.log(resProvince)
// 细节: option 标签的 value 属性记得设置值
let provinceStr = resProvince.data.map(item =>
`
">${item}
`
).join('')
// console.log(provinceStr)
// innerHTML 决定下拉框的内容
provinceSelect.innerHTML = `${provinceStr}`
// 渲染市
let { data: resCity } = await axios.get('/api/city', {
// 查询参数
params: { pname: province }
})
// 细节: option 标签的 value 属性记得设置值
let cityStr = resCity.data.map(item => {
return `
">${item}
`
}).join('')
// console.log(provinceStr)
citySelect.innerHTML = `${cityStr}`
// 渲染区
let { data: resArea } = await axios.get('/api/area', {
// 查询参数
params: { pname: province, cname: city }
})
// 细节: option 标签的 value 属性记得设置值
let cityArea = resArea.data.map( item =>
`
">${item}
`
).join('')
// console.log(provinceStr)
areaSelect.innerHTML = `${cityArea}`
// 省市区的数据成功展示到下拉框之后才能成功渲染
// value 决定了下拉框的值
provinceSelect.value = province
citySelect .value = city
areaSelect.value = area
// 图片
avatarImg.src = avatar
// 解决bug2 一开始渲染信息的时候 就把图片赋给隐藏域
hiddenAvatar.value = avatar
}
// 调用函数
render()
// ========================================= 省市切换功能 =========================================
// 省切换 ==> 当省份改变的时候,去获取到新的省份下的城市数据
// 给省注册 change 事件
provinceSelect.addEventListener('change', async function() {
// alert('省改变了') // 问,如何获取到新的省份(当前选择的省份)
// console.log(provinceSelect.value)
// 获取到市数据
let { data: resCity } = await axios.get('/api/city', {
params: {
// 查询参数 记得修改 pname 查询参数的值
pname: provinceSelect.value
}
})
let cityStr = resCity.data.map(item => `">${item}`).join('')
// innerHTML 内容
citySelect.innerHTML = `${cityStr}`
// 重置掉 区的数据
areaSelect.innerHTML = `}`
})
// 市切换
citySelect.addEventListener('change', async function() {
let { data: resArea} = await axios.get('/api/area', {
params: {
pname: provinceSelect.value,
cname: citySelect.value
}
})
let areaStr = resArea.data.map(item => `">${item}`).join()
// innerHTML 内容
areaSelect.innerHTML = `}${areaStr}`
})
// ========================================= 头像上传功能 =========================================
// 点击图片,模拟点击文件域
avatarImg.addEventListener('click', function() {
upload.click()
})
// 头像change
upload.addEventListener('change', async function() {
// 步骤:
// 1. 获取到用户选择的文件 ==> this.files[0]
// 2. FormData 存用户选择的文件
// 3. axios 发送请求实现上传头像功能 ==> 在线接口文档 http://ajax-api.itheima.net
// 1.
console.log(this.files[0])
// 判断用户是否有选择文件,没有的话不需要发送请求
if(!this.files[0]) {
// if 成立说明没有选择文件
return // 后续代码不执行
}
// 2.
const fd = new FormData()
fd.append('avatar', this.files[0]) // avatar 键,必须有引号
// 3.
let { data: res} = await axios.post('/api/file', fd)
console.log(res)
// console.log(res.data.url)
// 修改头像
avatarImg.src = res.data.url // res.data.url 是我们选择的图片它的url地址
// 把头像的路径存到隐藏域中, serialize 插件收集到该数据
hiddenAvatar.value = res.data.url
})
// ========================================= 保存功能 =========================================
// 步骤:
// 1. 给 form 表单注册 submit 事件
// 2. 阻止表单的默认行为
// 3. 收集表单数据 ==> serialize()
// 4. axios发送请求,提交表单数据进行保存
// 1.
form.addEventListener('submit', async function(e) {
// 2.
e.preventDefault()
// 3.
let data = serialize(this, { hash: true}) // 格式是 键值对字符串 'name=lw&age=19'
console.log(data)
// serialize 收集的数据,缺少 avatar 头像的数据
// 如何解决,使用隐藏域的知识点 type="hidden" 作用:存avatar头像数据,从而serialize 收集到该数据
// 4.
let { data: res } = await axios.put('/api/settings', data) // 请求体数据就是前面得到的 data
console.log(res)
})
</script>
</body>
</html>