Ajax基础入门(day4) - async 和 await

这是我们 Ajax 笔记的最后一章


文章目录

  • 一、async 和 await
    • 1. async 和 await 的基本使用
    • 2. 使用 async 和 await 解决回调地狱
      • async 和 await 的优化
    • 3. async 改写聊天机器人案例
  • 二、事件循环队列 - 宏任务微任务
    • 1. Event Loop 事件循环队列
    • 2. 宏任务 和 微任务
    • 3. 宏任务微任务-面试题
      • 面试题3.1
      • 面试题3.2
      • 面试题3.3
      • 面试题3.4
  • 三、案例 - 个人设置
    • 遇到BUG
      • BUG1: 提交的数据不全
      • 解决 BUG1:
      • BUG2: 头像问题
      • 解决 BUG2
    • 代码优化

一、async 和 await

Ajax基础入门(day4) - async 和 await_第1张图片

1. async 和 await 的基本使用

async 和 await 被称为异步的终极解决方案必须要学会使用
async 和 await 是一对关键字,需要配合使用
异步代码先不处理,先执行同步代码再执行异步操作。
Ajax基础入门(day4) - async 和 await_第2张图片

测试:

  1. 初始代码:
    Ajax基础入门(day4) - async 和 await_第3张图片
    效果是同步的
    Ajax基础入门(day4) - async 和 await_第4张图片
  2. 使用 async 和 await
    Ajax基础入门(day4) - async 和 await_第5张图片
    Ajax基础入门(day4) - async 和 await_第6张图片
    出现异步效果
    Ajax基础入门(day4) - async 和 await_第7张图片
    解释:
    Ajax基础入门(day4) - async 和 await_第8张图片
    await 阻塞:必须把我这个 57 行代码执行完了,再往下执行。
    Ajax基础入门(day4) - async 和 await_第9张图片
    Ajax基础入门(day4) - async 和 await_第10张图片
    总结:
    Ajax基础入门(day4) - async 和 await_第11张图片

2. 使用 async 和 await 解决回调地狱

以前得在 then 里面 return 结果
Ajax基础入门(day4) - async 和 await_第12张图片
Ajax基础入门(day4) - async 和 await_第13张图片

async 和 await 的优化

async 函数demo必须调用,否则函数不执行
Ajax基础入门(day4) - async 和 await_第14张图片

3. async 改写聊天机器人案例

Ajax基础入门(day4) - async 和 await_第15张图片
Ajax基础入门(day4) - async 和 await_第16张图片


二、事件循环队列 - 宏任务微任务

1. Event Loop 事件循环队列

Ajax基础入门(day4) - async 和 await_第17张图片
Ajax基础入门(day4) - async 和 await_第18张图片

2. 宏任务 和 微任务

Ajax基础入门(day4) - async 和 await_第19张图片
Ajax基础入门(day4) - async 和 await_第20张图片
Ajax基础入门(day4) - async 和 await_第21张图片
Ajax基础入门(day4) - async 和 await_第22张图片
Ajax基础入门(day4) - async 和 await_第23张图片

3. 宏任务微任务-面试题

面试题3.1

Ajax基础入门(day4) - async 和 await_第24张图片
Ajax基础入门(day4) - async 和 await_第25张图片
Ajax基础入门(day4) - async 和 await_第26张图片
整个代码分为两个宏任务,第一个宏任务是整个 script 标签代码,而延时函数属于第二个宏任务,
p 属于微任务,等第一个宏任务执行之后再执行 p。第一个宏任务和微任务结束之后,进行第二个宏任务,所以延时函数是最后输出的。

面试题3.2

当我们 resolve 成功的时候,将会触发 Promise 的 then 方法,而 then 方法属于微任务,要等宏任务结束再进行。
Ajax基础入门(day4) - async 和 await_第27张图片
Ajax基础入门(day4) - async 和 await_第28张图片

面试题3.3

技巧:先拆解出各个宏任务,然后按顺序拆解同一个宏任务下的微任务,先把微任务放一边,最后执行。
Ajax基础入门(day4) - async 和 await_第29张图片
Ajax基础入门(day4) - async 和 await_第30张图片

面试题3.4

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)

Ajax基础入门(day4) - async 和 await_第31张图片
resolve(5) 对应的是 p2 的成功结果 then(),属于微任务
resolve(6) 对应的是第一个 Promise,成功结果输出 (arg)

Ajax基础入门(day4) - async 和 await_第32张图片
Ajax基础入门(day4) - async 和 await_第33张图片
坑点1(reject)解析:
Promise有两个参数(resolve, reject),第一个位置就是 resolve,代码中只不过是用 reject 命名了 resolve 这个参数,本质上还是 resolve。(这里是一个坑,面试要注意

坑点2(resolve(4))解析:
resolve(4) 作用是把 p2 这个 Promise 对象状态变成成功的,但是我们之前的 resolve(5) 已经转化了它的状态为成功,由于状态凝固的原因, resolve(4) 是无法生效的。因此不会打印数字结果 4。
Ajax基础入门(day4) - async 和 await_第34张图片


三、案例 - 个人设置

Ajax基础入门(day4) - async 和 await_第35张图片
注意:
Ajax基础入门(day4) - async 和 await_第36张图片

遇到BUG

BUG1: 提交的数据不全

实现保存功能这块,涉及到新知识
代码编写过程中:
Ajax基础入门(day4) - async 和 await_第37张图片
Ajax基础入门(day4) - async 和 await_第38张图片
Ajax基础入门(day4) - async 和 await_第39张图片
出错原因是:发送请求的时候,少了一个接口文档要求的 avatar 数据

解决 BUG1:

使用隐藏域
Ajax基础入门(day4) - async 和 await_第40张图片
Ajax基础入门(day4) - async 和 await_第41张图片
Ajax基础入门(day4) - async 和 await_第42张图片
修改代码:
Ajax基础入门(day4) - async 和 await_第43张图片
Ajax基础入门(day4) - async 和 await_第44张图片

BUG2: 头像问题

Ajax基础入门(day4) - async 和 await_第45张图片
Ajax基础入门(day4) - async 和 await_第46张图片
将头像路径存到隐藏域中的代码是放在头像更改函数中的,但是头像没有发生变化,这个代码也就不会触发。所以隐藏域中没有 value 值,也就是头像的url地址。

解决 BUG2

Ajax基础入门(day4) - async 和 await_第47张图片
完整代码如下:

<!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>

代码优化

Ajax基础入门(day4) - async 和 await_第48张图片
Ajax基础入门(day4) - async 和 await_第49张图片

这样的赋值如果一多,就会很麻烦
可以考虑优化一下
Ajax基础入门(day4) - async 和 await_第50张图片


Ajax基础入门(day4) - async 和 await_第51张图片

你可能感兴趣的:(Ajax入门到精通,ajax,javascript,前端)