1 目录
1.1 同步代码和异步代码
1.1.1 同步代码
同步代码;逐行执行,需原地等待结果后,才继续向下执行。
1.1.2 异步代码
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数
1.1.3 案例——同步异步执行顺序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">我是按钮</button>
<script>
// 以下代码执行顺序是1、4、2、3(若三不点击,则执行顺序是1、4、2)
const result = 0 + 1
console.log(result)
setTimeout(() => {
console.log(2)
}, 2000)
document.querySelector('.btn').addEventListener('click', () => {
console.log(3)
})
document.body.style.backgroundColor = 'pink'
console.log(4)
</script>
</body>
</html>
结果:
1.2 回调函数地狱和promise链式调用
1.2.1 回调函数地狱
1.2.1.1缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
回调地狱:回调函数(回调函数为一个异步任务)嵌套回调函数
<!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>回调地狱</title>
</head>
<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>
/**
* 目标:演示回调函数地狱
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
* 概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
* 缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
*/
axios({
url: 'http://hmajax.itheima.net/api/province',
method: 'GET',
}).then(res => {
// 根据省份id获取市
const pname = res.data.list[0]
// 省份
document.querySelector('.province').innerHTML = pname
// 根据省份获取城市
axios({
url: 'http://hmajax.itheima.net/api/city',
method: 'GET',
params: { pname }
}).then(res => {
const cname = res.data.list[0]
document.querySelector('.city').innerHTML = cname
// 根据省份和城市获取地区
axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'GET',
params: { pname, cname }
}).then(res => {
document.querySelector('.area').innerHTML = res.data.list[0]
})
})
})
</script>
</body>
</html>
1.2.2 promise-链式调用
1.2.2.1 promise回顾:
Promise 是 JavaScript 中处理异步操作的一种解决方案,它代表一个异步操作的最终完成(或失败)及其结果值。Promise 对象有三个状态:
- pending(进行中):初始状态,既不是成功,也不是失败
- fulfilled(已成功):操作成功完成
- rejected(已失败):操作失败
Promise 的主要优点:
- 链式调用:可以连续调用 .then(),使代码更清晰
- 错误处理:可以使用 .catch() 统一处理错误
- 避免回调地狱:不需要嵌套多层回调函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const p = new Promise((resolve, reject) => {
resolve('成功1')
})
p
.then(res => {
console.log(res)
return new Promise((resolve, reject) => {
resolve(res + ' 成功2')
})
})
// promise的链式调用:再写一个then接收上一个then的返回值
.then(res => {
console.log(res)
})
</script>
</body>
</html>
结果:
1.2.2.2 promise的链式调用可以解决回调地狱问题
<!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>Promise链式调用_解决回调地狱</title>
</head>
<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链式调用结构
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
// 因为多个函数都要调用pname(省份名字),所以定义一个全局变量
let pname = ''
axios({
url: 'http://hmajax.itheima.net/api/province',
method: 'get'
}).then(res => {
pname = res.data.list[0]
document.querySelector('.province').innerHTML = pname
// axios的返回结果是一个promise对象,所以可以继续then
return axios({
url: 'http://hmajax.itheima.net/api/city',
method: 'get',
params: {
pname: res.data.list[0]
}
})
})
// 再使用then接收上一次的返回结果
.then(res => {
document.querySelector('.city').innerHTML = res.data.list[0]
return axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'get',
params: {
params: pname,
cname: res.data.list[0]
}
})
.then(res => {
document.querySelector('.area').innerHTML = res.data.list[0]
})
})
</script>
</body>
</html>
1.3 async和await使用
前言:promise链式调用虽然可以解决回调地狱,但是代码量较大,代码冗杂 async和await关键字让我们可以使用一种更加简洁的方式写出基于promise的异步行为,而无需刻意的调用promise
1.3.1 async和await的使用方式
<!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>async函数和await_解决回调函数地狱</title>
</head>
<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>
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
// async修饰函数,所以需要定义一个async函数
async function render() {
// await的等待结果会直接返回到变量中
const res = await axios({
url: 'http://hmajax.itheima.net/api/province',
method: 'get',
})
document.querySelector('.province').innerHTML = res.data.list[0]
const resCity = await axios({
url: 'http://hmajax.itheima.net/api/city',
method: 'get',
params: {
pname: res.data.list[0]
}
})
document.querySelector('.city').innerHTML = resCity.data.list[0]
const resArea = await axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'get',
params: {
pname: res.data.list[0],
cname: resCity.data.list[0]
}
})
document.querySelector('.area').innerHTML = resArea.data.list[0]
}
render()
</script>
</body>
</html>
结果:
1.3.2 async和await捕获错误
async和await只能解决成功的回调 本质只能使用.then而没有使用.catch的方法;所以会使用到“try,catch”捕获错误
<!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>async函数和await_解决回调函数地狱</title>
</head>
<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>
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
// async修饰函数,所以需要定义一个async函数
async function render() {
// await的等待结果会直接返回到变量中
try {
const res = await axios({
url: 'http://hmajax.itheima.net/api/province',
method: 'get',
})
document.querySelector('.province').innerHTML = res.data.list[0]
const resCity = await axios({
url: 'http://hmajax.itheima.net/api/city',
method: 'get',
params: {
pname: res.data.list[0]
}
})
document.querySelector('.city').innerHTML = resCity.data.list[0]
const resArea = await axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'get',
params: {
pname: res.data.list[0],
cname: resCity.data.list[0]
}
})
document.querySelector('.area').innerHTML = resArea.data.list[0]
} catch (err) {
console.log(err)
}
}
render()
</script>
</body>
</html>
1.4 事件循环——EventLoop
好处:掌握JavaScript是如何安排和运行代码的
定义:执行和收集异步任务的模型(机制),在调用栈空闲时,会反复调用任务队列里的回调函数的执行机制,叫做事件循环(因为JavaScript是单线程的,为了不阻塞JavaScript引擎,设计执行代码的模型)
1.5 宏任务和微任务
先执行第一个宏任务(script 或者js文件),
如果宏任务中有微任务,那么在执行完当前宏任务之后,会优先执行当前宏任务的微任务;微任务执行完成后,继续执行下一个宏任务
1.6 promise.all 静态方法
<!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>Promise的all方法</title>
</head>
<body>
<ul class="my-ul"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
const arr = [
axios({
url: 'http://hmajax.itheima.net/api/weather',
method: 'get',
params: {
city: '110100'
}
}),
axios({
url: 'http://hmajax.itheima.net/api/weather',
method: 'get',
params: {
city: '310100'
}
}),
axios({
url: 'http://hmajax.itheima.net/api/weather',
method: 'get',
params: {
city: '440100'
}
}),
axios({
url: 'http://hmajax.itheima.net/api/weather',
method: 'get',
params: {
city: '440300'
}
})
]
// 等待机制=>all方法会等待所有的promise对象的成功返回结果才会执行then方法,一旦有一个失败了都会执行错误结果
Promise.all(arr).then(resArr => {
document.querySelector('.my-ul').innerHTML = resArr.map(item => {
return `<li>${item.data.data.area}-----${item.data.data.weather}</li>`
}).join('')
}).catch(err => {
console.log(err)
})
</script>
</body>
</html>
结果:
1.7 案例——省市区切换
1.7.1 数据获取和列表渲染
/**
* 目标1:完成省市区下拉列表切换
* 1.1 设置省份下拉菜单数据
* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
* 1.3 切换城市,设置地区下拉菜单数据
*/
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(res => {
// console.log(res.data.list);
// 将省份数据保存为一个变量
const list = res.data.list
// 拼接字符串 并且渲染到页面中
document.querySelector('.province').innerHTML += list.map(item => {
return `<option value="${item}">${item}</option>`
}).join('')
})
// 切换省份,设置城市下拉菜单数据,清空地区下拉菜单(change的意思是当值改变的时候触发)
document.querySelector('.province').addEventListener('change', async e => {
// 清空下拉菜单
document.querySelector('.city').innerHTML = `<option value="">城市</option>`
console.log(e.target.value);
const { data: { list } } = await axios({
url: 'http://hmajax.itheima.net/api/city',
get: 'get',
params: { pname: e.target.value }
})
console.log(list)
document.querySelector('.city').innerHTML += list.map(item => `<option>${item}</option>`).join('')
})
// 切换市,设置区域的下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
// 地区列表去除先的列表数据
document.querySelector('.area').innerHTML = `<option value="">地区</option>`
// console.log(e.target.value)//只能获取到城市而不是省份
// 如何获取省份
// 不使用.then获取省份,就使用await和async
const { data: { list } } = await axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'get',
params: {
pname: document.querySelector('.province').value,
cname: e.target.value
}
})
// 渲染页面;
document.querySelector('.area').innerHTML += list.map(item => {
return `<option value="${item}">${item}</option>`
}).join('')
})
1.7.2 数据提交
/**
* 目标1:完成省市区下拉列表切换
* 1.1 设置省份下拉菜单数据
* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
* 1.3 切换城市,设置地区下拉菜单数据
*/
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(res => {
// console.log(res.data.list);
// 将省份数据保存为一个变量
const list = res.data.list
// 拼接字符串 并且渲染到页面中
document.querySelector('.province').innerHTML += list.map(item => {
return `<option value="${item}">${item}</option>`
}).join('')
})
// 切换省份,设置城市下拉菜单数据,清空地区下拉菜单(change的意思是当值改变的时候触发)
document.querySelector('.province').addEventListener('change', async e => {
// 清空下拉菜单
document.querySelector('.city').innerHTML = `<option value="">城市</option>`
console.log(e.target.value);
const { data: { list } } = await axios({
url: 'http://hmajax.itheima.net/api/city',
get: 'get',
params: { pname: e.target.value }
})
console.log(list)
document.querySelector('.city').innerHTML += list.map(item => `<option>${item}</option>`).join('')
})
// 切换市,设置区域的下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
// 地区列表去除先的列表数据
document.querySelector('.area').innerHTML = `<option value="">地区</option>`
// console.log(e.target.value)//只能获取到城市而不是省份
// 如何获取省份
// 不使用.then获取省份,就使用await和async
const { data: { list } } = await axios({
url: 'http://hmajax.itheima.net/api/area',
method: 'get',
params: {
pname: document.querySelector('.province').value,
cname: e.target.value
}
})
// 渲染页面;
document.querySelector('.area').innerHTML += list.map(item => {
return `<option value="${item}">${item}</option>`
}).join('')
})
// 目标二:提交数据,显示结果
const infoFrom = document.querySelector('.info-form')
document.querySelector('.submit').addEventListener('click', async e => {
// 使用插件收集表单数据,(要先在index里引入插件)
// empty: true获取为空的数据
const Data = serialize(infoFrom, { hash: true, empty: true })
const res = await axios({
url: 'http://hmajax.itheima.net/api/feedback',
method: 'POST',
data: Data
})
alert(res.data.message)
// 提交成功后,清空表单数据
infoFrom.reset()
})
结果: