Search K
Appearance
Appearance
看如下标签回答如下问题?
<select>
<option value="北京">北京市</option>
<option value="南京">南京市</option>
<option value="天津">天津市</option>
</select>当选中第二个 option 时,JS 中获取下拉菜单 select 标签的 value 属性的值是多少?
option 时,JS 中获取下拉菜单 select 标签的 value 属性的值是 "南京"。value 属性的值是当前选中的 option 元素的 value 属性值。option 的 value 属性是 "南京"。页面上看到的是北京,还是北京市?
option 元素的文本内容决定的。option 时,页面上显示的是 北京市。所以,页面上看到的是 北京市。我给 select 标签的 value 属性赋予 "南京" 会有什么效果?
option 选项的 value 能匹配。select 标签的 value 属性赋予 "南京" 不会产生直观的效果,因为 value 属性通常是由当前选中的 option 决定的。学习目标
Promise 链式调用)async 和 await 使用EventLoop 的概念Promise.all 静态方法作用什么是同步代码?
什么是异步代码?
JS 中有哪些异步代码?
setTimeout / setInterval异步代码如何接收结果?
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 } }).then((result) => {
const cname = result.data.list[0];
document.querySelector('.city').innerHTML = cname;
// 获取第一个城市默认下属第一个地区名字
axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then((result) => {
document.querySelector('.area').innerHTML = result.data.list[0];
});
});
});什么是回调函数地狱?
回调函数地狱问题?
then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
/**
* 目标:掌握 Promise 的链式调用
* 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
// 1. 创建 Promise 对象 - 模拟请求省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市');
}, 2000);
});
// 2. 获取省份名字
const p2 = p.then((result) => {
console.log(result);
// 3. 创建 Promise 对象 - 模拟请求城市名字
// return Promise 对象最终状态和结果,影响到新的 Promise 对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '--- 北京');
}, 2000);
});
});
// 4. 获取城市名字
p2.then((result) => {
console.log(result);
});
// then() 原地的结果是一个新的 Promise 对象
console.log(p2 === p);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>33 Promise 链式调用解决回调地狱 获取地方列表</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
</head>
<body class="p-3">
<div class="container">
<h2>33 Promise 链式调用解决回调地狱 获取地方列表</h2>
<form class="form-group row my-5 " id="editForm">
<div class="mb-3 col">
<label for="province" class="form-label">省份</label>
<select class="form-select" id="province" name="province">
<option value="北京" selected>北京</option>
</select>
</div>
<div class="mb-3 col">
<label for="city" class="form-label">城市</label>
<select class="form-select" id="city" name="city">
<option value="北京市" selected>北京市</option>
</select>
</div>
<div class="mb-3 col">
<label for="area" class="form-label">地区</label>
<select class="form-select" id="area" name="area">
<option value="东城区" selected>东城区</option>
</select>
</div>
</form>
</div>
<!-- <script src="./index.js"></script> -->
<script src="./js/axios.min.js"></script>
<script>
/**
* 目标:把回调函数嵌套代码,改成 Promise 链式调用结构
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
const province = document.querySelector('#province');
const city = document.querySelector('#city');
const area = document.querySelector('#area');
axios
.get('http://hmajax.itheima.net/api/province')
.then((res) => {
console.log(res.data);
// province.innerHTML = res.data.list.map(item => `
// <option value="${item}">${item}</option>
// `).join('')
province.innerHTML = `<option value="${res.data.list[6]}">${res.data.list[6]}</option>`;
return axios.get(`http://hmajax.itheima.net/api/city?pname=${province.value}`);
})
.then((res) => {
console.log(res.data);
// city.innerHTML = res.data.list.map(item => `
// <option value="${item}">${item}</option>
// `).join('')
city.innerHTML = `<option value="${res.data.list[6]}">${res.data.list[6]}</option>`;
return axios.get(`http://hmajax.itheima.net/api/area?pname=${province.value}&cname=${city.value}`);
})
.then((res) => {
console.log(res.data);
area.innerHTML = res.data.list.map((item) => `<option value="${item}">${item}</option>`).join('');
});
</script>
</body>
</html>什么是 Promise 的链式调用?
then 方法返回新 Promise 对象特性,一直串联下去then 回调函数中,return 的值会传给哪里?
then 方法生成的新 Promise 对象Promise 链式调用有什么用?
Promise 链式调用如何解决回调函数地狱?
then 的回调函数中返回 Promise 对象,影响当前新 Promise 对象的值<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>34 async 函数和 await 解决回调函数地狱 获取地方列表</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
</head>
<body class="p-3">
<div class="container">
<h2>34 async 函数和 await 解决回调函数地狱 获取地方列表</h2>
<form class="form-group row my-5 " id="editForm">
<div class="mb-3 col">
<label for="province" class="form-label">省份</label>
<select class="form-select" id="province" name="province">
<option value="北京" selected>北京</option>
</select>
</div>
<div class="mb-3 col">
<label for="city" class="form-label">城市</label>
<select class="form-select" id="city" name="city">
<option value="北京市" selected>北京市</option>
</select>
</div>
<div class="mb-3 col">
<label for="area" class="form-label">地区</label>
<select class="form-select" id="area" name="area">
<option value="东城区" selected>东城区</option>
</select>
</div>
</form>
</div>
<!-- <script src="./index.js"></script> -->
<script src="./js/axios.min.js"></script>
<script>
/**
* 目标:掌握 async 和 await 语法,解决回调函数地狱
* 概念:在 async 函数内,使用 await 关键字,获取 Promise 对象"成功状态"结果值
* 注意:await 必须用在 async 修饰的函数内(await 会阻止"异步函数内"代码继续执行,原地等待结果)
*/
const province = document.querySelector('#province');
const city = document.querySelector('#city');
const area = document.querySelector('#area');
// 1. 定义 async 修饰函数
async function getData() {
// 2. 在 async 函数内,使用 await 关键字,获取 Promise 对象"成功状态"结果值
const res1 = await axios.get('http://hmajax.itheima.net/api/province');
console.log(res1.data);
province.innerHTML = `<option value="${res1.data.list[10]}">${res1.data.list[10]}</option>`;
const res2 = await axios.get(`http://hmajax.itheima.net/api/city?pname=${province.value}`);
console.log(res2.data);
city.innerHTML = `<option value="${res2.data.list[10]}">${res2.data.list[10]}</option>`;
const res3 = await axios.get(`http://hmajax.itheima.net/api/area?pname=${province.value}&cname=${city.value}`);
console.log(res3.data);
area.innerHTML = res3.data.list.map((item) => `<option value="${item}">${item}</option>`).join('');
}
getData();
</script>
</body>
</html>try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
try {
// 要执行的代码
} catch (error) {
// error 接收的是,错误消息
// try 里代码,如果有错误,直接进入这里执行
}try 里有报错的代码,会立刻跳转到 catch 中
尝试把代码中 url 地址写错,运行观察 try catch 的捕获错误信息能力
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>35 async 函数和 await 错误捕获 获取地方列表</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
</head>
<body class="p-3">
<div class="container">
<h2>35 async 函数和 await 错误捕获 获取地方列表</h2>
<form class="form-group row my-5 " id="editForm">
<div class="mb-3 col">
<label for="province" class="form-label">省份</label>
<select class="form-select" id="province" name="province">
<option value="北京" selected>北京</option>
</select>
</div>
<div class="mb-3 col">
<label for="city" class="form-label">城市</label>
<select class="form-select" id="city" name="city">
<option value="北京市" selected>北京市</option>
</select>
</div>
<div class="mb-3 col">
<label for="area" class="form-label">地区</label>
<select class="form-select" id="area" name="area">
<option value="东城区" selected>东城区</option>
</select>
</div>
</form>
</div>
<!-- <script src="./index.js"></script> -->
<script src="./js/axios.min.js"></script>
<script>
/**
* 目标:async 和 await 错误捕获
*/
const province = document.querySelector('#province');
const city = document.querySelector('#city');
const area = document.querySelector('#area');
async function getData() {
// 1. try 包裹可能产生错误的代码
try {
const res1 = await axios.get('http://hmajax.itheima.net/api/province');
console.log(res1.data);
province.innerHTML = `<option value="${res1.data.list[10]}">${res1.data.list[10]}</option>`;
const res2 = await axios.get(`http://hmajax.itheima.net/api/city?pname=${province.value}`);
console.log(res2.data);
city.innerHTML = `<option value="${res2.data.list[10]}">${res2.data.list[10]}</option>`;
// const res3 = await axios.get(`http://hmajax.itheima.net/api/area?pname=${province.value}&cname=${city.value}`);
const res3 = await axios.get(`http://hmajax.itheima.net/api/area1000?pname=${province.value}&cname=${city.value}`);
console.log(res3.data);
area.innerHTML = res3.data.list.map((item) => `<option value="${item}">${item}</option>`).join('');
} catch (err) {
// 2. 接着调用 catch 块,接收错误信息
// 如果 try 里某行代码报错后,try 中剩余的代码不会执行了
console.dir(error);
}
}
getData();
</script>
</body>
</html>await 的作用是什么?
then 方法来提取 Promise 对象成功状态的结果try 和 catch 有什么作用?

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>36 事件循环练习</title>
</head>
<body>
<script>
console.log(1); // 进入调用栈,执行同步代码,然后出栈,打印 1
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(2);
}, 0);
function myFn() {
console.log(3);
}
function ajaxFn() {
const xhr = new XMLHttpRequest(); // ajax 异步请求,将回调函数放入任务队列
xhr.open('GET', 'http://hmajax.itheima.net/api/province');
xhr.addEventListener('loadend', () => {
console.log(4);
});
xhr.send();
}
for (let i = 0; i < 1; i++) { // for 循环,执行同步代码,然后出栈,打印 5
console.log(5);
}
ajaxFn(); // ajaxFn 函数,执行同步代码,然后出栈,打印 4
document.addEventListener('click', () => { // 事件监听,用户点击后输出 6
console.log(6);
});
myFn(); // myFn 函数,执行同步代码,然后出栈,打印 3
// 调用栈:
// 1. console.log(1);
// 2. for (let i = 0; i < 1; i++) { console.log(5); }
// 3. myFn(); function myFn() { console.log(3); }
// 任务队列:
// 1. setTimeout(() => { console.log(2); }, 0);
// 宿主环境(浏览器):
// 1. xhr.addEventListener('loadend', () => { console.log(4); });
// 2. document.addEventListener('click', () => { console.log(6); });
// 打印结果:
// 1 5 3 2 4(用户点击后)6
</script>
</body>
</html>
ES6 之后引入了 Promise 对象,让 JS 引擎也可以发起异步任务
异步任务划分为了
宏任务和微任务具体划分:

事件循环模型
/**
* 目标:阅读并回答打印的执行顺序
*/
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
const p = new Promise((resolve, reject) => {
resolve(3);
});
p.then((res) => {
console.log(res);
});
console.log(4);
注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!
下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队
总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>37 事件循环面试题</title>
</head>
<body>
<script>
console.log(1); // 进入调用栈,执行同步代码
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(2);
const p = new Promise((resolve) => resolve(3)); // Promise 构造函数,执行同步代码
p.then((result) => console.log(result)); // Promise.then 回调函数,执行异步代码
}, 0);
const p = new Promise((resolve) => { // Promise 构造函数,执行同步代码
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(4);
}, 0);
resolve(5); // Promise.resolve 执行同步代码
});
p.then((result) => console.log(result)); // Promise.then 回调函数,执行异步代码
const p2 = new Promise((resolve) => resolve(6)); // Promise 构造函数,执行同步代码
p2.then((result) => console.log(result)); // Promise.then 回调函数,执行异步代码
console.log(7); // 进入调用栈,执行同步代码
// 调用栈:
// 1. console.log(1);
// 2. console.log(7);
// 宿主环境(浏览器):
// 1. setTimeout(() => { console.log(2); const p = new Promise((resolve) => resolve(3)); p.then((result) => console.log(result)); }, 0);
// 2. setTimeout(() => { console.log(4); }, 0); }
// 任务队列 - 微任务:
// 1. const p = new Promise((resolve) => { resolve(5); }).then((result) => console.log(result));
// 2. const p2 = new Promise((resolve) => resolve(6)).then((result) => console.log(result));
// 任务队列 - 宏任务:
// 1. console.log(2); const p = new Promise((resolve) => resolve(3)); p.then((result) => console.log(result));
// 2. console.log(4);
// 打印结果:
// 1 7 5 6 2 3 4
</script>
</body>
</html>
什么是事件循环?
为什么有事件循环?
JavaScript 内代码如何执行?
什么是宏任务?
什么是微任务?
Promise 对象.then() 的回调JavaScript 内代码如何执行?

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

语法:
const p = Promise.all([Promise 对象,Promise 对象,...])
p.then(result => {
// result 结果:[Promise 对象成功结果,Promise 对象成功结果,...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>38 同时显示多地天气 - Promise.all</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
</head>
<body class="p-3">
<div class="container">
<h2>38 同时显示多地天气 - Promise.all</h2>
<ul class="list-group my-5">
<li class="list-group-item">...</li>
</ul>
</div>
<script src="./js/axios.min.js"></script>
<script>
/**
* 目标:掌握 Promise 的 all 方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京 (110100) 上海 (310100) 广州 (440100) 深圳 (440300)
*/
// 1. 请求城市天气,得到 Promise 对象
// const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
// const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
// const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
// const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })
const getWeather = (cityCode) => {
return axios.get(`http://hmajax.itheima.net/api/weather?city=${cityCode}`);
};
const cityCodes = ['110100', '310100', '440100', '440300'];
const cityPromises = cityCodes.map((cityCode) => getWeather(cityCode));
// 2. 合并多个 Promise 对象,得到一个新的 Promise 对象
const allPromise = Promise.all(cityPromises);
// 3. 处理请求结果
allPromise
.then((res) => {
console.log(res);
document.querySelector('.list-group').innerHTML = res
.map((item) => {
return `<li class="list-group-item">【${item.data.data.area}】天气:${item.data.data.weather}</li>`;
})
.join('');
})
.catch((err) => {
console.dir(err);
});
</script>
</body>
</html>目标:把所有商品分类 "同时" 渲染到页面上
<!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>39 商品分类导航</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!-- 大容器 -->
<div class="container">
<div class="sub-list">
<div class="item">
<h3>分类名字</h3>
<ul>
<li>
<a href="#"><img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png"
referrerpolicy="no-referrer" />
<p>巧克力</p>
</a>
</li>
</ul>
</div>
</div>
</div>
<script src="./js/axios.min.js"></script>
<script src="./js/index.js"></script>
</body>
</html>/**
* 目标:把所有商品分类“同时”渲染到页面上
* 1. 获取所有一级分类数据
* 2. 遍历 id,创建获取二级分类请求
* 3. 合并所有二级分类 Promise 对象
* 4. 等待同时成功后,渲染页面
*/
// 1. 获取所有一级分类数据
// api: http://hmajax.itheima.net/api/category/top - GET
axios.get('http://hmajax.itheima.net/api/category/top').then((res) => {
console.log(res);
// 2. 遍历 id,创建获取二级分类请求
// api: http://hmajax.itheima.net/api/category/sub/:id - GET
const subCategoryPromises = res.data.data.map((item) => {
return axios.get(`http://hmajax.itheima.net/api/category/sub?id=${item.id}`);
});
console.log(subCategoryPromises); // [Promise, Promise, Promise, Promise]
// 3. 合并所有二级分类 Promise 对象
Promise.all(subCategoryPromises).then((res) => {
console.log(res); // res: [Array, Array, Array, Array]
// 4. 等待同时成功后,渲染页面
document.querySelector('.sub-list').innerHTML = res
.map((item) => {
const category = item.data.data;
return `
<div class="item">
<h3>${category.name}</h3>
<ul>
${category.children
.map((item) => {
return `<li><a href="#"><img src="${item.picture}" referrerpolicy="no-referrer" /><p>${item.name}</p></a></li>`;
})
.join('')}
</ul>
</div>
`;
})
.join('');
});
});Promise.all 什么时候使用?
掌握 async 和 await 的使用
async 函数是用来定义一个返回 Promise 对象的函数。在函数体内部,可以使用 await 来暂停函数的执行,等待 Promise 对象的解决。
async function exampleAsyncFunction() {
console.log('Start');
// 使用 await 暂停函数执行,等待 Promise 对象解决
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve('Async operation completed');
}, 1000);
});
console.log(result);
console.log('End');
}
exampleAsyncFunction();await 关键字只能在 async 函数内部使用。它用于等待一个 Promise 对象的解决。当遇到 await 时,async 函数会暂停执行,直到 Promise 对象解决。在上述例子中,await 会等待 setTimeout 的延时操作完成。
await 时需要确保其所在的函数是 async 函数,否则会导致语法错误。async function exampleAsyncFunction() {
console.log('Start');
const result = await someAsyncOperation(); // 必须在 async 函数内使用 await
console.log(result);
console.log('End');
}使用 async 和 await 可以更清晰地处理 Promise 的链式调用,避免了回调地狱(Callback Hell)。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
// fetchData 函数使用 await 依次等待 fetch 和 response.json() 的结果,使得异步操作看起来像同步代码一样,更易读理解 EventLoop 和宏任务微任务执行顺序
事件循环(Event Loop)是一个用于处理异步操作的机制。
在事件循环中,任务被分为两类:宏任务(Macro Task)和微任务(Micro Task)。它们分别被放入不同的执行队列,并按照一定的规则执行。
Event Loop 执行过程
了解 Promise.all 的作用和使用场景
Promise.all 是一个用于处理多个 Promise 并行执行的工具方法。它接收一个包含多个 Promise 的可迭代对象(通常是数组),并返回一个新的 Promise。
这个新 Promise 会在所有输入的 Promise 都成功解决(resolved)时被解决,或者在任意一个输入的 Promise 被拒绝(rejected)时被拒绝。
Promise.all 返回的 Promise 的解决值是一个包含所有输入 Promise 解决值的数组。
使用场景:
Promise.all 是一个很有用的工具。Promise.all 可以在所有数据都准备好时触发后续逻辑。完成案例 - 学习反馈
<!DOCTYPE html>
<html lang="zh-CN">
<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">
<!-- 初始化样式 -->
<link rel="stylesheet" href="./css/reset.min.css">
<!-- 引入 bootstrap.css -->
<link href="./css/bootstrap.min.css" rel="stylesheet">
<!-- 核心样式 -->
<link rel="stylesheet" href="./css/index-min.css">
<title>40 学习反馈</title>
</head>
<body>
<div class="container">
<div class="toast position-fixed top-0 start-50 translate-middle-x" role="alert" aria-live="assertive"
aria-atomic="true" data-bs-delay="2000">
<div class="toast-body">
操作成功
</div>
</div>
<h4 class="stu-title">学习反馈</h4>
<img class="bg" src="./img/head.webp" alt="">
<div class="item-wrap">
<div class="hot-area">
<span class="hot">热门校区</span>
<ul class="nav">
<li><a target="_blank" href="http://bjcp.itheima.com/">北京</a> </li>
<li><a target="_blank" href="http://sh.itheima.com/">上海</a> </li>
<li><a target="_blank" href="http://gz.itheima.com/">广州</a> </li>
<li><a target="_blank" href="http://sz.itheima.com/">深圳</a> </li>
</ul>
</div>
<form class="info-form">
<div class="area-box">
<span class="title">地区选择</span>
<select name="province" class="province">
<option value="">省份</option>
</select>
<select name="city" class="city">
<option value="">城市</option>
</select>
<select name="area" class="area">
<option value="">地区</option>
</select>
</div>
<div class="area-box">
<span class="title">您的称呼</span>
<input type="text" name="nickname" class="nickname" value="播仔">
</div>
<div class="area-box">
<span class="title">宝贵建议</span>
<textarea type="text" name="feedback" class="feedback" placeholder="您对AJAX阶段课程宝贵的建议"></textarea>
</div>
<div class="area-box">
<button type="button" class="btn btn-secondary submit">
确定提交
</button>
</div>
</form>
</div>
</div>
<script src="./js/axios.min.js"></script>
<script src="./js/form-serialize.js"></script>
<script src="./js/bootstrap.min.js"></script>
<!-- 核心代码 -->
<script src="./js/render.js"></script>
<script src="./js/submit.js"></script>
</body>
</html>/**
* 目标 1:完成省市区下拉列表切换
* 1.1 设置省份下拉菜单数据
* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
* 1.3 切换城市,设置地区下拉菜单数据
*/
// 1 设置省份下拉菜单数据
const provinceElement = document.querySelector('.province');
const cityElement = document.querySelector('.city');
const aeraElement = document.querySelector('.area');
axios
.get('http://hmajax.itheima.net/api/province')
.then((res) => {
console.log(res);
provinceElement.innerHTML = `<option value="">请选择省份</option>${res.data.list
.map((item) => `<option value="${item}">${item}</option>`)
.join('')}`;
})
.catch((error) => {
console.dir(error);
});
// 2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
provinceElement.addEventListener('change', async function () {
const province = this.value;
try {
const res = await axios.get(`http://hmajax.itheima.net/api/city?pname=${province}`);
console.log(res);
cityElement.innerHTML = `<option value="">请选择城市</option>${res.data.list
.map((item) => `<option value="${item}">${item}</option>`)
.join('')}`;
aeraElement.innerHTML = '<option value="">请选择区县</option>';
} catch (error) {
console.dir(error);
}
});
// 3 切换城市,设置地区下拉菜单数据
cityElement.addEventListener('change', async function () {
const city = this.value;
try {
const res = await axios.get(`http://hmajax.itheima.net/api/area?pname=${provinceElement.value}&cname=${city}`);
console.log(res);
aeraElement.innerHTML = `<option value="">请选择区县</option>${res.data.list
.map((item) => `<option value="${item}">${item}</option>`)
.join('')}`;
} catch (error) {
console.dir(error);
}
});/**
* 目标 2:收集数据提交保存
* 2.1 监听提交的点击事件
* 2.2 依靠插件收集表单数据
* 2.3 基于 axios 提交保存,显示结果
*/
// 1 监听提交的点击事件
const submitBtn = document.querySelector('.submit');
submitBtn.addEventListener('click', async () => {
// 2 依靠插件收集表单数据
const form = document.querySelector('.info-form');
const formData = serialize(form, { hash: true, empty: true });
const toastDom = document.querySelector('.toast');
const toast = new bootstrap.Toast(toastDom);
// toastDom.querySelector('.toast-body').style.backgroundColor = 'var(--bs-primary)';
toastDom.querySelector('.toast-body').classList.add('p-3', 'rounded-3');
// 3 基于 axios 提交保存,显示结果
try {
const res = await axios.post('http://hmajax.itheima.net/api/feedback', formData);
console.log(res);
// HTTP 状态码:200
if (res.status === 200) {
// alert(res.data.message);
toastDom.querySelector('.toast-body').textContent = res.data.message;
toastDom.querySelector('.toast-body').style.backgroundColor = 'var(--bs-success)';
toast.show();
}
} catch (err) {
console.dir(err);
toastDom.querySelector('.toast-body').textContent = err.response.data.message;
toastDom.querySelector('.toast-body').style.backgroundColor = 'var(--bs-danger)';
toast.show();
}
});在线答题:Day04_AJAX 进阶
以下哪个代码能正确运行?
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const res1 = await p1;const p2 = new Promise((resolve, reject) => {
reject(2);
});
const res2 = await p2;const p3 = new Promise((resolve, reject) => {
resolve(3)
})
async const myFn = () => {
const res3 = await p3
}
myFn()const p4 = new Promise((resolve, reject) => {
resolve(4);
});
const myFn4 = async () => {
const res4 = await p4;
};
myFn4();await 关键字只能在 async 函数内部使用。选项 B 中的代码使用了正确的语法结构,即在 async 函数内使用 await。await 放在 async 函数内部。async const myFn = () => {...} 缺少括号。const p4 = new Promise(...} 缺少括号,且 async const myFn4 = () => {...} 缺少括号。以下代码输出结果是?
const fn = async () => {
const res = await 10;
console.log(res);
};
fn();10await 后面不是 Promise, 所以执行失败undefinedawait 后面紧跟的是一个非 Promise 对象(数字 10),但是在 async 函数内部,非 Promise 对象会被自动包装成一个 resolved 的 Promise 对象。因此,整个 async 函数会正常执行,并输出结果。以下代码输出结果是?
let p = new Promise((resolve, reject) => {
resolve(1000);
});
let p2 = new Promise((resolve, reject) => {
reject(1000);
});
const theFn = async () => {
const res = await p;
console.log(res);
const res2 = await p2;
console.log(res2);
};
theFn();await p 会正常执行并输出 1000,但第二个 await p2 由于 p2 是一个 rejected 的 Promise,会导致 Promise 的状态变为 rejected,触发 await 后面的代码块的异常处理。theFn 函数是一个异步函数,所以不会影响整个程序的执行。以下代码能正确捕获到异常的是?
let p = new Promise((resolve, reject) => {
reject(new Error('请检查'));
});
// A:
// async const fnA = () => {
// try {
// const res = await p
// } catch (err) {
// console.error(err)
// }
// }
// B:
// const fnB = async () => {
// try {
// } catch (err) {
// const res = await p;
// console.error(err);
// }
// };
// C:
// const fnC = async () => {
// try {
// const res = await p;
// } catch {
// console.error(err);
// }
// };
// D:
// const fnD = async () => {
// try {
// const res = await p;
// } catch (err) {
// console.error(err);
// }
// };await p 处会触发 reject 状态,进入到 catch 块中,正确捕获到异常并输出错误信息。async const 是不正确的语法。try 块为空,没有实际的异步操作。catch 块没有声明 err 参数。以下代码都有哪些任务?
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
let p = new Promise((resolve, reject) => {
resolve(3);
});
p.then((res) => {
console.log(res);
});
console.log(4);答案是 C. 同步任务、微任务(Promise 的回调函数)、宏任务(setTimeout 回调函数)。
console.log(1); 是一个同步任务,会在主线程上立即执行。setTimeout(() => { console.log(2); }, 0); 是一个宏任务,会被放入任务队列中,等待主线程任务执行完毕后执行。let p = new Promise((resolve, reject) => { resolve(3); }); 是一个同步任务,Promise 的执行是同步的,但它的 then 方法中的回调是微任务,会在当前任务执行完成后立即执行。console.log(4); 是一个同步任务,会在主线程上立即执行。所以,该代码包含同步任务、微任务(Promise 的回调函数)、宏任务(setTimeout 回调函数)。
以下代码打印结果是?
// 读题回答打印顺序
console.log(1);
myFn();
setTimeout(() => {
theFn();
}, 0);
new Promise((resolve, reject) => {
resolve(2);
}).then((res) => {
console.log(res);
});
function myFn() {
console.log(3);
}
const theFn = () => {
console.log(4);
};答案是 B. 1, 3, 4, 2。
console.log(1); 是一个同步任务,会在主线程上立即执行。myFn(); 是一个同步任务,会在主线程上立即执行,打印 3。setTimeout(() => { theFn(); }, 0); 是一个宏任务,会被放入任务队列中,等待主线程任务执行完毕后执行。new Promise((resolve, reject) => { resolve(2); }).then((res) => { console.log(res); }); 是一个同步任务,Promise 的执行是同步的,但它的 then 方法中的回调是微任务,会在当前任务执行完成后立即执行,打印 2。theFn 是一个函数声明,会被提升到作用域顶部,所以在 setTimeout 中能够访问到,theFn(); 执行时打印 4。因此,输出结果是:1, 3, 4, 2。
以下代码打印结果是?
// 读题回答打印顺序
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
return new Promise((resolve, reject) => {
reject(new Error(''));
});
}
console.log(3);
setTimeout(function () {
console.log(4);
}, 0);
async1();
new Promise(function (resolve) {
console.log(5);
resolve();
}).then(function () {
console.log(6);
});
console.log(7);答案是 A. 3, 1, 7, 5, 6, 4。
console.log(3); 是一个同步任务,会在主线程上立即执行。setTimeout(function () { console.log(4); }, 0); 是一个宏任务,会被放入任务队列中,等待主线程任务执行完毕后执行。async1(); 被调用,async1 是一个异步函数,其中的 console.log(1); 是同步执行的,然后遇到 await async2();,async2 返回的 Promise 是 rejected 状态,触发 async1 中的异常,但由于异步函数的特性,异常会被 Promise 包装,不会中断主线程,所以会继续执行 console.log(2);。new Promise(function (resolve) { console.log(5); resolve(); }).then(function () { console.log(6); }); 是一个同步任务,Promise 的执行是同步的,但它的 then 方法中的回调是微任务,会在当前任务执行完成后立即执行,打印 5 和 6。setTimeout 中的回调,打印 4。console.log(7); 是一个同步任务,会在主线程上立即执行。所以,输出结果是:3, 1, 7, 5, 6, 4。
以下代码打印结果是?
// 读题回答打印顺序
setTimeout(() => {
console.log(1);
new Promise((resolve, reject) => {
resolve(2);
}).then((res) => {
console.log(res);
setTimeout(() => {
console.log(3);
}, 1000);
});
}, 0);
console.log(4);
setTimeout(() => {
console.log(5);
}, 5000);
console.log(6);答案是 C. 4, 6, 1, 2, 3, 5。
setTimeout,打印 1。然后执行内部的 Promise,由于是 resolved 状态,执行其 then 回调,打印 2,并设置另一个 setTimeout,打印 3。setTimeout,由于设置了延时为 5000 毫秒,所以会在 5000 毫秒后执行,打印 5。所以,输出结果是:4, 6, 1, 2, 3, 5。
以下代码打印结果是?
// 读题回答打印顺序
new Promise((resolve, reject) => {
console.log(1);
new Promise((resolve, reject) => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 0);
console.log(4);
});
console.log(5);
});
setTimeout(() => {
console.log(6);
}, 1000);
console.log(7);答案是 C. 1, 2, 5, 4, 7, 3, 6。
setTimeout,将其放入宏任务队列中,接着打印 4。setTimeout,打印 3。setTimeout,由于设置了延时为 1000 毫秒,所以会在 1000 毫秒后执行,打印 6。所以,输出结果是:1, 2, 5, 4, 7, 3, 6。
以下代码打印结果是?
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
console.log(4);
setTimeout(() => {
console.log(5);
}, 0);
console.log(6);答案是 D. 1, 3, 4, 2, 6, 5。
console.log(1);,打印 1。setTimeout,将其放入宏任务队列中,继续执行同步任务,打印 3。setTimeout,打印 2。setTimeout,将其放入宏任务队列中,但由于前面的任务已经执行完成,所以可以立即执行,打印 5。所以,输出结果是:1, 3, 4, 2, 6, 5。
<!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>事件循环经典 经典面试题 1</title>
</head>
<body>
<script>
console.log(1) // 进入调用栈,执行同步代码
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(2)
}, 0)
console.log(3) // 进入调用栈,执行同步代码
</script>
<script>
console.log(4) // 进入调用栈,执行同步代码
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(5)
}, 0)
console.log(6) // 进入调用栈,执行同步代码
// 调用栈:
// 1. console.log(1);
// 2. console.log(3);
// 3. console.log(4);
// 4. console.log(6);
// 宿主环境(浏览器)
// 1. setTimeout(() => { console.log(2); }, 0);
// 2. setTimeout(() => { console.log(5); }, 0);
// 任务队列 - 宏任务:
// 1. console.log(2);
// 2. console.log(5);
// 打印结果:
// 1 3 4 6 2 5
</script>
</body>
</html><!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>事件循环经典 经典面试题 2</title>
</head>
<body>
<!-- 今日头条 -->
<!-- 提示:await 右结合,右边代码执行后原地等待(await 取代 then 函数,相当于微任务) -->
<script>
console.log(1) // 进入调用栈,执行同步代码
async function fnOne() {
console.log(2)
await fnTwo() // 右结合先执行右侧的代码,然后等待
console.log(3)
}
async function fnTwo() {
console.log(4)
}
fnOne()
setTimeout(() => { // 宿主环境(浏览器),2s 后将回调函数放入任务队列
console.log(5)
}, 2000)
let p = new Promise((resolve, reject) => { // new Promise() 里的函数体会马上执行所有代码
console.log(6)
resolve()
console.log(7)
})
setTimeout(() => { // 宿主环境(浏览器),0s 后将回调函数放入任务队列
console.log(8)
}, 0)
p.then(() => { // Promise.then 回调函数,执行异步代码
console.log(9)
})
console.log(10) // 进入调用栈,执行同步代码
// 调用栈
// 1. console.log(1)
// 2. fnOne() { console.log(2) }
// 3. fnTwo() { console.log(4) }
// 4. let p = new Promise((resolve, reject) => { console.log(6); resolve(); console.log(7); }
// 5. console.log(10)
// 任务队列 - 宏任务:
// 1. setTimeout(() => { console.log(5) }, 2000)
// 2. setTimeout(() => { console.log(8) }, 0)
// 任务队列 - 微任务:
// 1. console.log(3)
// 2. p.then(() => { console.log(9) })
// 打印结果:
// 1 2 4 6 7 10 3 9 8 5
</script>
</body>
</html><!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>事件循环经典 经典面试题 3</title>
</head>
<body style="width: 100%; height: 100vh;">
<!-- 需要给 body 设置宽高,如果没有设置,则无法点击,绑定的点击事件会无效 -->
<script>
// Twitter 面试题 - 20% 答对
document.body.addEventListener('click', (e) => {
let p = new Promise(resolve => resolve(1))
p.then(result => console.log(result)) // 微任务:Promise 的回调函数(即 then 方法中的函数)是异步代码
console.log(2) // 同步代码
})
document.body.addEventListener('click', () => {
let p = new Promise(resolve => resolve(3))
p.then(result => console.log(result))
console.log(4)
})
// 调用栈:
// 1. console.log(2);
// 微任务:
// 1. p.then(result => console.log(result))
// 打印结果:
// (用户点击后) 2 1 4 3
</script>
</body>
</html>目标:完成如下评论列表效果
要求:
接口文档:https://apifox.com/apidoc/shared-1b0dd84f-faa8-435d-b355-5a8a329e34a8/api-82668108
配套资料:配套标签和样式在文件夹内
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>评论列表</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
<style>
.badge {
float: right;
margin-right: 5px;
}
.my-page {
margin: 0 5px;
}
.all-page-content {
margin-left: 5px;
}
</style>
</head>
<body style="padding: 15px;">
<!-- 评论面板 -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">发表评论</h3>
</div>
<form class="panel-body cmt-form">
<div>评论人:</div>
<input type="text" class="form-control" name="username" autocomplete="off" />
<div>评论内容:</div>
<textarea class="form-control" name="content"></textarea>
<button type="submit" class="btn btn-primary submit">发表评论</button>
</form>
</div>
<!-- 评论列表 -->
<ul class="list-group">
<!-- <li class="list-group-item">
<span>评论内容</span>
<span class="badge del" style="cursor:pointer; background-color: lightgray;">删除</span>
<span class="badge" style="background-color: #F0AD4E;">评论时间:xxx</span>
<span class="badge" style="background-color: #5BC0DE;">评论人:xxx</span>
</li> -->
</ul>
<!-- 分页器 -->
<nav>
<ul class="pagination">
<li>
<button class="last">
<span>«</span>
</button>
</li>
<li class="my-page">
<span class="page-show"></span>
</li>
<li>
<button class="next">
<span>»</span>
</button>
</li>
<li class="all-page-content">
<span>共计:<span class="all-page"></span>页</span>
</li>
</ul>
</nav>
<script src="./js/axios.min.js"></script>
<script src="./js/form-serialize.js"></script>
<script src="./js/getComment.js"></script>
<script src="./js/addComment.js"></script>
<script src="./js/delComment.js"></script>
</body>
</html>// 1 默认上来展示所有评论列表数据(注意不区分用户了)感受下大家数据互相影响,也可以看到别人评论
// api: https://hmajax.itheima.net/api/cmtlist?page=${nowPage} GET
let nowPage = 1;
let allPage;
const listGroup = document.querySelector('.list-group');
function fetchCommentList() {
axios.get(`https://hmajax.itheima.net/api/cmtlist?page=${nowPage}`).then((res) => {
console.log(res);
// 总页码数
allPage = res.data.allPage;
document.querySelector('.all-page').innerText = allPage;
// 清空列表
listGroup.innerHTML = '';
// 渲染列表
listGroup.innerHTML = res.data.data
.map((item) => {
return `<li class="list-group-item">
<span>${item.content}</span>
<span class="badge del" style="cursor:pointer; background-color: lightgray;" data-id=${item.id}>删除</span>
<span class="badge" style="background-color: #F0AD4E;">评论时间:${item.time}</span>
<span class="badge" style="background-color: #5BC0DE;">评论人:${item.username}</span>
</li>`;
})
.join('');
// 设置页码
document.querySelector('.page-show').innerHTML = nowPage;
});
}
// 页面加载时默认拉取第一页的评论数据
fetchCommentList();
// 绑定 上一页/下一页按钮的点击事件
const lastElement = document.querySelector('.last');
const nextElement = document.querySelector('.next');
// 分页切换评论列表数据功能
if (lastElement && nextElement) {
lastElement.addEventListener('click', () => {
nowPage > 1 ? nowPage-- && fetchCommentList() : console.log('已经是第一页了');
});
nextElement.addEventListener('click', () => {
nowPage < allPage ? nowPage++ && fetchCommentList() : console.log('已经是最后一页了');
});
}// 新增评论功能
// api: https://hmajax.itheima.net/api/addcmt POST
// data:username content
const submitElement = document.querySelector('.submit');
submitElement.addEventListener('click', async (e) => {
e.preventDefault(); // 阻止默认行为
// 获取表单数据
const form = document.querySelector('.cmt-form');
const formData = serialize(form, { hash: true, empty: true });
try {
const response = await axios.post('https://hmajax.itheima.net/api/addcmt', formData);
console.log(response);
// 重新拉取评论列表数据
nowPage = 1;
fetchCommentList();
// 表单复位
form.reset();
} catch (error) {
console.error(error);
}
});// 删除评论功能
// 删除最后一条评论,列表要自动回到上一页
// api: https://hmajax.itheima.net/api/delcmt?id=${id} GET
listGroup.addEventListener('click', async (e) => {
console.log(e.target);
if (e.target.classList.contains('del')) {
try {
const response = await axios.get(`https://hmajax.itheima.net/api/delcmt?id=${e.target.getAttribute('data-id')}`);
console.log(response);
allPage = response.data.allPage;
nowPage = nowPage > allPage ? allPage : nowPage;
fetchCommentList();
} catch (error) {
console.dir(error);
}
}
});