目标:阅读并回答代码执行和打印的顺序
<script> 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>
打印结果为: 1,4,2,点击按钮一次就打印一次3.
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 => { const area = result.data.list[0] document.querySelector('.area').innerHTML = area }) }) }).catch(error => { console.log(error) }) // 这样一来,就进入了回调地狱了,而且错误无法捕获
- 回调地域主要就是在回调函数中,不断的使用回到函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CI9Ao6Nx-1691654693290)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-07_22-29-32.png)]
需求:把省市的嵌套结构,改成链式调用的线性结构
const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('北京市') }, 2000) }) // 2、获取省份的名字 const p2 = p.then(result => { console.log(result) // 3、创建Promise对象,获取城市名字 return new Promise((resolve,reject) => { setTimeout(() => { resolve(result + '---北京') },2000) }) }) // 4、获取城市名字 p2.then(result => { console.log(result) }) // then函数原地的结果是一个新的Promise对象 console.log(p2 === p) //地址不一样 </script>
目标:使用Promise解决链式调用,解决回调函数地域的问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV7efGq0-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-07_23-02-04.png)]
实现方式:每个Promise对象中管理一个一步任务,用then返回Promise对象,串联起来。
需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
let pname = '' // 1、得到 - 获取省份Promise对象 axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => { pname = result.data.list[0] document.querySelector('.province').innerHTML = pname // 2.得到 - 获取城市Promise对象 return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }) }).then(result => { const cname = result.data.list[0] document.querySelector('.city').innerHTML = cname // 3、得到 -获取地区Promise对象 return axios({url: 'http://hmajax.itheima.net/api/area',params:{pname, cname}}) }).then(result => { const area = result.data.list[0] document.querySelector('.area').innerHTML = area }) </script>
// 1.定义一个async修饰的函数 async function getData() { // Promise对象的返回成功的结果被await接收。 const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'}) const pname = pObj.data.list[0] const cObj = await axios({url: 'http://hmajax.itheima.net/api/city',params: {pname}}) const cname = cObj.data.list[0] const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname}}) const area = aObj.data.list[0] document.querySelector('.province').innerHTML = pname document.querySelector('.city').innerHTML = cname document.querySelector('.area').innerHTML = area } getData()
awwit会接受Promise对象返回成功的值。
try { //要执行的代码 } catch (error) { //error接收的是,错误信息 //try里的代码,如果有错误,直接进入这里执行 }
同步放在执行栈,异步被宿主环境读取,放在任务队列中
由于JavaScript是单线程的,(某一刻只能 执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLX51V2x-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-08_17-19-44.png)]
在JavaScript中,数据类型有分为简单数据类型和引用数据类型,当我们js引擎识别到
- 简单数据类型 放在栈空间存储值(因为简单数据类型没有地址)
- 引用数据类型(有地址存储在内存中),所以,引用数据类型地址放在栈空间,值放在堆空间,我们程序员能支配栈空间,堆空间由系统来支配,所以深浅考拷贝的问题就在于地址和值的问题,对于此,我们程序员只能操作引用数据类型的地址,而值,不能直接去改变,会产生很多问题。
事件循环:就是js执行机制分配好存储空间,来决定执行代码
- 当代码识别为简单数据类型:则将代码放在执行栈中
- 当代码被识别为引用数据类型(复杂数据类型),则将代码放在任务队列中
- 当执行栈中的代码被执行完毕后,那么js执行机制会向任务队列中读取里面的任务代码,根据所需要执行的事件进行取出任务到执行栈中运行代码。执行完毕后再次读取任务队列中是否还有任务需要执行,如果还有任务,那么继续取出来运行,多次重复的读取操作,就被成为事件循环。
// 体验JS是单线程的,必须先遍历完,才能改变颜色
document.querySelector('.time-btn').addEventListener('click', () => {
for (let i = 0; i < 300000; i++) {
console.log(i)
}
document.body.style.background = 'pink'
})
// 为了避免线程卡死,出现了异步代码,把耗时操作放到异步中,先保证主线程执行完所有同步代码
document.querySelector('.time-btn').addEventListener('click', () => {
setTimeout(() => {
for (let i = 0; i < 300000; i++) {
console.log(i)
}
}, 0)
document.body.style.background = 'pink'
})
<script>
console.log(1)
setTimeout(() =>{
console.log(2)
},2000)
console.log(3)
//打印顺序:1,3,2
console.log(1)
setTimeout(() => {
console.log(2)
},0)
console.log(3)
//打印顺序还是: 1,3,2 (因为setTimeout是引用数据类型,放到任务队列中等待执行栈中的同步任务执行完,在读取任务队列中的代码执行,并且一次只能执行一个,多个的话,就重复,这样就形成了事件循环)
</script>
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
function myFn() {
console.log(3)
}
function ajaxFn() {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
console.log(4)
})
xhr.send()
}
for (let i = 0; i < 1; i++) {
console.log(5)
}
ajaxFn()
document.addEventListener('click', () => {
console.log(6)
})
myFn()
// 答1,5,3,2,4,6(不点击事件不执行)
ES6之后引入了Promise对象,让JS引擎也可以发起异步任务。
异步任务分为;
宏任务:由浏览器环境执行的异步代码。
微任务:由JS引擎环境执行的代码
执行顺序:执行栈空闲时,宏任务只有等到所有的微任务执行完毕后才会到宏任务中读取任务。
任务(代码) | 执行所在的环境 |
---|---|
JS脚本执行事件(script) | 浏览器 |
setTimeout/setlnterval | 浏览器 |
Ajax请求完成事件 | 浏览器 |
用户交互事件 | 浏览器 |
Promise本身是同步代码,而then和catch回调函数是异步的。放在微任务执行。
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)
console.log(1)
setTimeout(() => {
console.log(2)
},0)
const p = new Promise((resolve,reject) => {
console.log(3)
resolve(4)
})
p.then(result => {
console.log(result)
})
console.log(5)
// 目标:回答代码执行顺序
console.log(1)
setTimeout(() => {
console.log(2)
const p = new Promise(resolve => resolve(3))
p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
setTimeout(() => {
console.log(4)
}, 0)
resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7)
// 1 7 5 6 2 3 4
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CpSvq3KJ-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-10_09-11-37.png)]
需要同时结果的时候,就是用Promise.all即可。
概念: 合并多个Promise对象,等待所有同时成功完成(或某一个失败),做后续逻辑
const p = Promise.all([Promise对象,Promise对象,....])
p.then(result => {
//result结果:[Promise对象成功的结果,Promise对象成功的结果,...]
}).catch(error => {
//第一个失败的Promise对象,抛出的异常
})
// 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' } })
// 2. 使用Promise.all,合并多个Promise对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
// 注意:结果数组顺序和合并时顺序是一致
console.log(result)
const htmlStr = result.map(item => {
return `${item.data.data.area} --- ${item.data.data.weather}`
}).join('')
document.querySelector('.my-ul').innerHTML = htmlStr
}).catch(error => {
console.dir(error)
})
核心步骤:
* 1. 获取所有一级分类数据
* 2. 遍历id,创建获取二级分类请求
* 3. 合并所有二级分类Promise对象
* 4. 等待同时成功后,渲染页面
// 1. 获取所有一级分类数据
axios({ url: 'http://hmajax.itheima.net/api/category/top' }).then(result => {
console.log(result)
//2. 遍历id,创建获取二级分类请求
const list = result.data.data.map(item => {
return axios({
url: 'http://hmajax.itheima.net/api/category/sub',
params: {
id: item.id
}
})
})
console.log(list)
//3. 合并所有二级分类Promise对象 Promise 对象就是所有axios函数的返回值
const p = Promise.all(list)
p.then(result => {
console.log(result)
// 4. 等待同时成功后,渲染页面
const htmlStr = result.map(item => {
// 取出关键的数据对象
const dataObj = item.data.data
return `
分类名字
${dataObj.children.map(item => {
return `-
${item.picture}
">
${item.name}
`
}).join('')}
`
}).join('')
console.log(htmlStr)
document.querySelector('.sub-list').innerHTML = htmlStr
})
})
* 目标1:完成省市区下拉列表联动切换
* 1.1 设置省份下拉菜单数据
* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
* 1.3 切换城市,设置地区下拉菜单数据
// 1.1 设置省份下拉菜单数据
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
console.log(result)
// 数组映射
const optionStr = result.data.list.map(pname => `">${pname}`).join()
document.querySelector('.province').innerHTML = `` + optionStr
})
// 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector('.province').addEventListener('change', async e => {
// 立马获取到用户选择的省份名字
// 拿到省份数据
const result = await axios({url: 'http://hmajax.itheima.net/api/city',params: { pname: e.target.value}})
const optionStr = result.data.list.map(cname => `">${cname}`).join('')
// 把默认的城市选项 + 下属城市数据插入到select中
document.querySelector('.city').innerHTML = `` + optionStr
// 清空地区数据
document.querySelector('.area').innerHTML = ``
})
// 1.3 切换城市,设置地区下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
const result = await axios({url: 'http://hmajax.itheima.net/api/area',params: {
pname: document.querySelector('.province').value,
cname: e.target.value
}})
console.log(result)
const optionStr = result.data.list.map(aname => `">${aname}`).join('')
document.querySelector('.area').innerHTML = `` + optionStr
})
// 省市区联动效果。2023年8月10日15:36:15
* 目标2: 收集数据提交保存
* 2.1 监听提交的点击事件
* 2.2 依靠插件收集表单元素
* 2.3 基于axios提交缓存,显示结果
// 2.1 监听提交的点击事件
document.querySelector('.submit').addEventListener('click', async () => {
// 2.2 依靠插件收集表单元素
const form = document.querySelector('.info-form')
const data = serialize(form, { hash: true, empty: true })
console.log(data)
// 2.3 基于axios提交缓存,显示结果
try {
const result = await axios({
url: 'http://hmajax.itheima.net/api/feedback',
method: 'POST',
data//因为接口文档参数名和返回的结果名相同,直接传入data,data配置对象属性名和属性值相同,则简写。
})
console.log(result)
alert(result.data.message)
} catch(error) {
console.dir(error)
alert(error.response.data.message)
}
})