一.async和await
1.async和await语法
async function 函数名() {
const result = await Promise对象
// 拿到Promise对象内成功的结果继续向下执行
}
示例:
// 用await取代then函数, 来提取成功的值在原地
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的值')
}, 2000)
})
// 普通函数: async+await
async function fn() {
const result = await p
console.log(result)
}
fn()
// 箭头函数: async+await
const myFn = async () => {
const result = await p
console.log(result)
}
myFn()
async和await的作用是:取代then函数, 来提取Promise的值
2.async和await注意事项
await 必须用在async修饰的函数内
// 1. await必须用在被async修饰的函数内, 函数内无await, 则async可省略
// 报错: await is only valid in async functions
function myFn() {
const res = await 1
}
myFn()
async修饰后, 此函数为异步函数
// 在此函数内, 遇到await会暂停代码往下, 但不影响外面继续执行同步流程
// 等所有主线程同步代码走完, 再执行await并继续向下
console.log(1);
async function myFn() {
console.log(2);
const res = await 3
console.log(res);
console.log(4);
}
console.log(5);
myFn()
console.log(6);
await之后一般跟promise
await后面一般跟Promise对象
// 如果跟的是非Promise对象, 则等待所有同步代码执行后, 把此结果作为成功的结果留在原地使用
// 如果是Promise对象, 则等待Promise对象内触发resolve返回成功结果, 在原地使用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
console.log(1);
async function myFn() {
console.log(2);
const res = await p
console.log(res);
console.log(4);
}
console.log(5);
myFn()
console.log(6);
await不能捕获失败结果, 需要使用try+catch关键字捕获
/*
try和catch语法
try {
// 这里放可能在执行中报错的代码
// 如果报错会终止代码继续执行, 直接跳转进catch里执行
} catch (err) {
// err接收try大括号内报错抛出的异常代码
}
*/
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1)
reject(new Error('失败'))
}, 2000)
})
async function myFn() {
try {
const res = await p
console.log(res);
} catch (err) {
console.error(err)
}
}
myFn()
二.使用async和await优化回调地狱
改进前
// 使用Promise的链式调用解决问题
// axios函数在原地返回的就是一个Promise对象
let pname = ''
axios.get('http://ajax-api.itheima.net/api/province').then(res => {
// 2. 获取某个省, 对应的城市列表
pname = res.data.data[5];
return axios.get(`http://ajax-api.itheima.net/api/city?pname=${pname}`)
}).then(res => {
// 3. 获取某个市, 对应的地区列表
let cname = res.data.data[0]
return axios.get(`http://ajax-api.itheima.net/api/area?pname=${pname}&cname=${cname}`)
}).then(res => {
console.log(res);
})
改进后
// 使用Promise的链式调用解决问题
// axios函数在原地返回的就是一个Promise对象
async function f() {
const provinces = await axios.get('http://ajax-api.itheima.net/api/province')
const pname = provinces.data.data[5]
const citys = await axios.get(`http://ajax-api.itheima.net/api/city?pname=${pname}`)
const cname = citys.data.data[0]
const areas = await axios.get(`http://ajax-api.itheima.net/api/area?pname=${pname}&cname=${cname}`)
return areas
}
f()
三.EventLoop事件循环
浏览器中的EventLoop(这个比较偏理论一点,我是这样理解的):js是单线程的,一次只能做一件事。js在浏览器这个宿主环境中运行。浏览器是多线程的,用户交互,定时器,网络请求等等浏览器中的事件会产生对应的任务,任务多了要在任务队列中排队,浏览器的主线程依次取出任务来执行,此过程不断重复从而形成一个循环,称为EventLoop。
JavaScript 是一门单线程执行的脚本语言。也就是说,同一时间只能做一件事情。
javaScript要运行在宿主环境中(浏览器,nodejs)下。浏览器内部有执行js代码的引擎(V8引擎)
浏览器事件循环:
排队是任务是以事件及其回调的方式存在的。
当事件(用户的点击,图片的成功加载)发生时,将其回调添加到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执行
四.微任务和宏任务
1.异步任务
不是马上执行,是放入到队列中等待;
如果所有的任务都要按序等待,那么也不行,需要有一个能插队的机制。所以又将异步任务分为微任务和宏任务,同时对应微任务队列和宏任务队列。
当主线程空闲时,先执行微任务队列中的任务,再去执行宏任务队列中的任务。
2.微任务队列和宏任务队列
微任务代码(js语法)
- Promise对象.then()
宏任务代码(宿主环境)
- script
- dom事件
- ajax
- setTimout
五.个人信息修改模板(附带头像上传)
完成案例效果图:
HTML和CSS代码:
<!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>08.案例_个人信息修改</title>
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.1.3/dist/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" 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="https://unpkg.com/bootstrap@5.1.3/dist/js/bootstrap.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js"></script>
<script src="./lib/form-serialize.js"></script>
</body>
</html>
落地js代码:
//渲染个人信息
<script>
axios.defaults.baseURL = 'http://ajax-api.itheima.net'
// 封装函数document.querySelector('#' + id)
function $(id) {
return document.querySelector('#' + id)
}
async function getInfo() {
const res = await axios.get('/api/settings')
const obj = res.data.data
const { area, avatar, city, nickname, province } = obj
// console.log(obj);
// 数据回填
$('avatar').src = avatar
$('nickname').value = nickname
// 把省的数据整体填入
const resProvince = await axios.get('/api/province')
$('province').innerHTML += resProvince.data.data.map(item => `<option value="${item}">${item}</option>`)
$('province').value = province
// 把市的数据整体填入
const resCity = await axios.get(`/api/city?pname=${province}`)
$('city').innerHTML += resCity.data.data.map(item => `<option value="${item}">${item}</option>`)
$('city').value = city
// 把地区的数据整体填入
const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
$('area').innerHTML += resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
$('area').value = area
}
getInfo()
// 给省这个下拉框添加change事件
$('province').addEventListener('change', async function () {
// 获取最新选择的省
const province = $('province').value
// 发请求,得到对应的市
const resCity = await axios.get(`/api/city?pname=${province}`)
// console.log(resCity);
// 添加(渲染)到页面
$('city').innerHTML = resCity.data.data.map(item => `<option value="${item}">${item}</option>`)
// 默认选择第一个市
const city = resCity.data.data[0]
$('city').value = city
// 发请求,得到对应的地区
const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
// console.log(resArea);
// 添加(渲染)到页面
$('area').innerHTML = resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
$('area').value = resArea.data.data[0]
})
// 给市这个下拉框添加change事件
$('city').addEventListener('change', async function () {
// 获取最新选择的省
const province = $('province').value
// 拿到最新的市
const city = $('city').value
// 发请求,得到对应的市
// console.log(city);
// 发请求,得到对应的地区
const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
// console.log(resArea);
// 添加(渲染)到页面
$('area').innerHTML = resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
$('area').value = resArea.data.data[0]
})
// 修改图像
// 给修改头像img注册点击事件
$('avatar').addEventListener('click', function () {
$('upload').click()
})
$('upload').addEventListener('change', async function (e) {
let file = e.target.files[0]
let fd = new FormData()
fd.append('avatar', file)
const portrait = await axios({
url: '/api/file',
method: 'POST',
data: fd
})
console.log(portrait);
$('avatar').src = portrait.data.data.url
})
// 保存按钮
$('btnSave').addEventListener('click', async function (e) {
// 阻止默认行为
e.preventDefault()
const nickname = $('nickname').value
const province = $('province').value
const city = $('city').value
const area = $('area').value
const avatar = $('avatar').src
try {
await axios.put('/api/settings', {
nickname,
province,
city,
area,
avatar
})
alert('YES')
} catch (err) {
alert('NO')
}
})
</script>
任何需求的思路是:先标签后样式, 再数据铺设/绑定事件, 根据画面拆分需求按步骤实现, 前端准备好就调用接口
六.自测题Ajax
这里有10道Ajax选择题(祝你得100分): 异步编程和事件循环